Category Archives: Arduino Ultrasonic Anemometer

My main project at the moment. I’m building a ultrasonic anemometer (wind meter) based on an arduino uno.

Ultrasonic Anemometer Part 30: Downsized Hardware

In my last post of this series I’ve looked at different transducers and finally decided on a entirely waterproof 14mm model. The much lower signal level from those kind of transducers makes it  necessary to reduce the distance between the transducers in order to still receive a reasonable signal amplitude.
So I took my previous lasercut design and reduced it in size so that the distance between the transducers is only 120mm. I went to the local FabLab and and lasered two copies of the downsized design.

Since the new transducers are only 14mm in diameter but those plastic pipes are only available in 16mm I had to pad the transducers with short sections of aluminium tube with an outer and inner diameter of 16 and 14mm, respectively.
I previsously connected the wind meter to an Arduino with a LCD display to test the I2C functionality as well as to read the wind speed and direction without having to connect the anemometer to a PC. Now I have used my I2C user interface for that purpose. It is powered from the wind meter’s 3.3 volt rail and is controlled via I2C including the backlight brightness and display reset. The user interface usually also includes a rotary encoder with push-button not present in this application.

In an attempt to boost signal amplitude I’ve also changed the first stage op amp’s gain from 11 to 31 by replacing the 10k resistor with a 30k one. I’ve argued in my last post that this is probably about as far as you can safely push it.
Even with the lowered transducer distance and increased gain the signal amplitude after the first stage is less than impressive. But we knew that already. And we have a second variable-gain stage at our disposal. Setting the second stage’s gain sufficiently high we hopefully get the 3 volts peak-to-peak amplitude we are aiming for.

By the way, I’ll started from scratch with the firmware. The reason for that is mainly USB. Currently, the anemometer acts as a HID device. While working on my solar charger I started to play around with HID / MSD (human interface device / mass storage device) composite devices. They still act as HIDs with all the functionality just like before. But when you plug it into a PC it also enumerates as a mass storage device, i.e. it behaves just like a memory stick. You can read and write the files on it from any computer, irrespective of the operating system and without needing any particular softwar or driver installed. So you can put all the configuration parameters in a text file that resides on what looks like a memory stick. I guess that’s the most user friendly way of dealing with those user-specific configuration parameters. Just plug it in, open a text file, change what you need, save the file and you’re done. This is how the solar charger will operate and  I’ve decided to implement the same here.
Microchip’s Harmony library includes all that functionality so I have no doubt it’s doable. But I’ve figured that it’s likely easier to do this as a new project and then add the previously implemented functionality step-by-step.

And then there’s another USB-related feature that I had in mind right from the start but which I haven’t event started to implement so far: A USB boot loader. That would enable people to do a firmware update without needing a in-circuit programmer such as the PICKit 3. Users could just download a new HEX file and upgrade the firmware without needing any special hardware or even skills.
USB boot loaders come in various flavours. The Harmony library includes support for an HID boot loader. But that’s not really ideal if you ask me. It means that you need to run some software on your PC that communicates with the anemometer and sends the new firmware via USB. The need for software means that it matters what kind of operating system you run. And needing additional software is undesirable in the first place.

There are also MSD bootloaders which do not have all those downsides. The device acts as a memory stick to the PC. The user then just copies the HEX file to that memory stick and that’s it. Just drag-and-drop, no extra software, no matter what OS.

The problem is that the Harmony library does not support that out of the box. Indeed, Microchip doesn’t seem to have implemented that for any of their chips. That’s kind of strange because other chip manufacturers have done so long time since. There is at least one implementation for PICs out there in the web but I haven’t looked at it yet because the website it was hosted on no longer exists it seems. Probably I’ll have to implement this myself but the Harmony support for MSD will hopefully do much of the heavy lifting.

So that’s the plan. Implement a bootloader, preferably of the MSD variety. And then run the anemometer appliction as a HID/MSD composite device. You may say that I set the wrong priorities given the fact that the actual measuring is not yet as good as it needs to be. But this kind of fundamental architecture is easier to get right from the very beginning. So that’s why.
If you’re interested in this lasercut design you can find iton github.com or more precisely on https://github.com/soldernerd/AnemometerLasercut. This version is called Anemometer_03.scad and the PDF that was used for the actual laser cutting is Anemometer_03.pdf.

Ultrasonic Anemometer Part 29: Transducer Comparison

It’s been way too long since the last post in my Ultrasonic Anemometer series. But better late then never.

So far I have always used the same type of transducers. When I started this project I looked around and found the Multicomp MCUSD16A40S12RO. They were comparatively cheap and readily available so I ordered some and used nothing else for the next two or so years.

However, they are not hermetically sealed and therefore not the most suitable type to use outdoors all year around. Some of you have already experimented with other, truely watertight types and have reported that the signal level with those is dramatically lower. So I decided to see what’s available and do a more systematic comparison. That’s what this post is about.

The Contestants

  1. Multicomp MCUSD16A40S12R0 (Farnell 2362677),  16mm, not waterproof, no wires attached.
  2. Multicomp MCUSD14A40S09RS (Farnell 2362679), 14mm, waterproof, no wires attached.
  3. Multicomp MCUSD14A40S09RS-30C (Farnell 2362678), 14mm, waterproof, with attached wires.
  4. Multicomp MCUSD18A40S09RS-30C (Farnell 2362680), 18mm, waterproof, with attached wires.

You might wonder why I’m only considering Multicomp models but those 3 models are pretty much all the 40kHz watertight transducers I can get from Farnell.

So what I did is I attached all 4 models to the same anemometer so that I can switch back and forth between them. That turned out to be rather tedious and time consuming and resulted in a big mess of wires so I was happy that I only had 4 types to worry about.

I’ve measured the signal after the first (fixed gain) stage of the op-amp. So the signal reported here is already boosted up by a factor of 11.

The Benchmark

Let’s first look at the model used so far. As we already know it delivers a solid signal amplitude.

Zooming in at the burst at the center of the screen (the one with the smalles amplitude) gives us a reading of 1.9 volts peak-to-peak.

14mm waterproof transducer without wires

Let’s look at the first waterproof model. The signal amplitude us barely noticable at first.

Zooming in (both horizontally and vertically) then reveals the amplitude: 47 millivolts, a factor of 40 less than the benchmark model.

14mm waterproof with wires

Next in line is a transducer that looks virtually identical but has a pair of drilled wires at the back as opposed to just two pins. The overview looks just like before.

Zooming in shows an even smaller signal amplitude of only 37millivolts. That’s surprising because it has somewhat better specs compared to the previous model.

18mm waterproof with wires

Once again, the overview shows hardly anything for the 18mm model.

Zooming in we find an amplitude of 38 millivolts, basically the same as the last model.

Conclusion

The first fact is obvious: with hermetically sealed transducers you can only expect a small fraction of the signal at the input.  I knew that but was still surprised to find how much smaller that singal is.  In this comparison it’s a factor of 40in the very best case.

But an anemometer is useless if it cannot withstand the elements so I guess we have to live with that. I will use the 14mm without wires type going forward. Not only did it perfom best in my comparison but it is also the most affordable (waterproof) model. Apart from that I like its small 14mm form factor allowing for slim designs posing less resistance to the airflow.

The road ahead

We obviously need to make some hardware changes to deal with the low signal amplitude. The most obvious solution is more gain. We can get more gain of the first (fixed gain) stage by just changing a resistor. However, we need to be careful not to push the op amp beyond its limits. I’ve looked at the LMC6482 datasheet again and found a gain/bandwidth product of 1.5MHz. That means we can get a maximum gain of 37.5 at 40kHz. You probably don’t want to push the op amp right to its limit so something like 31 (changing the 10k resistor to 30k)  is probably about the best we can do here.  That gives us an extra factor of 2.82.

Making the physical design smaller and therefore reducing the distance between the transducers is another thing we can do.  The current desing is 245mm which is rather large. Someone has reported to me that he reduced that all the way down to 100mm. I don’t think I will go that far but will try 120mm which is about half of what I have now. That should double the signal amplitude. And the smaller transducers should make the task easier.

The two measures combined give an extra gain of factor 5.6. That leaves us with another factor 7 to be compensated elsewhere. Fortunately, we still have a second op amp stage (the one controlled by software) which hasn’t been doing much so far.  With the previous transducer model it only had to deliver something like a factor 1.5. If we boos this up to 10 we pretty much have all we need.

A software bug

During my tests I also discovered some weird software behaviour when the signal (after both stages of amplification) is not in the expected range. USB may hang, among other things. The pulse-sending sequence can also get upset. Someone had reported this to me earlier but I wasn’t able to reproduce the problem until now. I don’t expect this to be a major thing to fix but you never know. But I’ll definitely look into this.

 

 

 

Ultrasonic Anemometer Part 28: New hardware tested

20160907_standaloneanemometer_024

I last time proudly presented the new RevB board and got a lot of feedback from people who want one, too. As mentioned I have all the components here to ship up to 10 kits but I was reluctant to send anything until I had the chance to do some hardware testing. Not much had changed since the last revision but I don’t like taking chances on things like this.

20160907_standaloneanemometer_029

In the mean time I managed to do some rudimentary testing and now feel confident to take orders. These tests concern the hardware only. What I said last time about the state of the software still applies. But let me tell you what I’ve been able to test so far.

rxtx_overview

Tests performed so far

  • The PIC32 can be programmed from a PICKit3 via the ICSP header without any issues.
  • Power consumption is as expected. Like the previous version it draws 45mA@12V (programmed) and the other two rails come up with +3.310 and -3.279V, respectively. Also as expected. Regulator stays cool.
  • With the PIC controlling the AXIS, DIRECTION and SIGNAL pins, the transducers receive the 12V signal from the mosfet drivers. HOWEVER: the signals AXIS and DIRECTION are incorrectly labeled both in Eagle as well as on the silk screen. Electrically everything is fine but the names have been confused.
  • The signal from the transducers is properly received (Rec pin on the board) and amplified by a factor of 11 by the first stage of the amplifier (S1 pin on the board).
  • The PIC is able to control the digipot over the internal I2C bus and the second amplifier stage also performs as expected.
  • The zero-crossing detector (ZCD) works.
  • The input to the PIC’s ADC (ADC+ and ADC-) look fine, too. HOWEVER: the labeling on the silk screen is wrong. Eagle is correct, it’s just the silk screen. Plus should be minus and vice-versa. Again, electrically everything is fine.
  • The PIC can communicate (as a slave) with an Arduino UNO connected to the external I2C bus.
  • Communication over USB to a YAT-terminal under Win 7 works.

rxtx_north

rx_closeup

The following has not been tested so far

  • For my tests I still used the transducers already used previously. I believe the ones I ordered last week are of the same type but I haven’t tested them.
  • EEPROM. I haven’t tried the newly added memory yet. I have confirmed that it has power and is connected to the same bus as the digipot so I have no reasons to assume there are issues with it. But testing it would require some software first.
  • The external SPI bus has never been tested, neither on the Rev A board nor with the new one. I don’t expect any problems but I haven’t done any testing so far.

20160907_standaloneanemometer_028

Is there anything important that I forgot to mention? In that case just ask.  A lot still needs to be done but at this point I’m confident that the board has no major flaws and performs much like the prototype.  Want to give it a try? I have some kits left for you.

i2c1_arduino

Continue here to the next post of this series.

Ultrasonic Anemometer Part 27: Ready to take pre-orders

20160903_StandaloneAnemometer_001Good news: the boards from dirtypcbs.com have arrived and look great. I also got all the components for the 11 boards. Why 11? I ordered about 10 (they call it a protopack) and was lucky enough to get 11. Thats dirtypcbs.

Last week I also upgraded my hobbyist Eagle license to a proper Premium LS license which means I can now legally start selling stuff. So I’m basically ready to ship the first kits.

20160903_StandaloneAnemometer_002

Today I assembled one of the boards and it at least looks great. All the footprints are correct and it was a pleasure to solder. Now what I want to do is to run some tests with it just to make sure it works as intended. I didn’t change much since the last version but I want to be sure first.

20160903_StandaloneAnemometer_003

About the kit

There’s one thing I want to be absolutely clear about. At this stage of development the wind meter is not yet ready to be deployed. While I think the hardware is final now, a lot more work is needed to get the software ready. So for the time being this kit is intended for people who want to join the development and testing. I’ve done most of the low-level, register-fiddling stuff but much remains to be done at a higher level. I know there are a lot of people out there with much more experience in signal processing than me and I’m looking forward to work on this challenge together. And a challenge it is. But the PIC32 still has plenty of RAM, Flash and CPU time left to try out new ideas and approaches until we find one that works well.

20160903_StandaloneAnemometer_004

The kit contains the board and all the necessary components. Details can be found in the BOM linked on the overview page. Once assembled it should look precisely like on the photos on this page. But as the name suggests, it comes as a kit, i.e. as components that you have to solder yourself. Most components come in relatively large SOIC packages but there are a few smaller MSOP and SOT-23 packages as well. They can all be soldered with a conventional soldering iron and strain solder just like I’ve done today.

20160903_StandaloneAnemometer_005

The microcontroller is not yet programmed so you will need a suitable programmer. Microchip’s PICKit3 (USD47.95) is the obvious choice here. This is also what I’m using and matches the board’s pinout. All the software (MPLAB X IDE and XC32 compiler) are available for download from Microchip free of charge.

Taking pre-orders now

I’ll start taking pre-orders now but as mentioned I won’t ship the kits until I’ve done some tests with my own board. Once that’s done I’ll let you know and if you’re still interested by then I’ll give you my PayPal details and ship the kits.

Some have mentioned that they already have some ultrasonic tranducers and/or want to try some specific model so you are free to order your kit with or without the transducers.

20160903_StandaloneAnemometer_007

There’s no online shop or anything like that so just use the contact form on the about me page.

Pricing

Now it starts getting interesting. I’ll quote all prices in USD, EUR and CHF.  Choose what’s cheapest or most convenient for you.

  • Kit without transducers: USD 70 / EUR 63 / CHF 69
  • Kit with transducers: USD 95 / EUR 85 / CHF 93

The prices above include worldwide shipping. The kits ship by Swiss Post Priority Mail in a padded envelope.  I’ve used this service before to locations like Brazil or India and never had problems. However, there are no tracking numbers.

Any other questions? Just ask.

20160903_StandaloneAnemometer_006

 

Ultrasonic Anemometer Part 26: Rev B Board ordered

I recently ordered my first PCB at dirtypcbs.com and the result was promising. So there was nothing stopping me from finalizing the Rev B of my standalone Ultrasonic Anemometer and ordering a protopack. I’ve placed the order a few days ago and expect the boards to arrive here in 2 to 3 weeks. This should be good news for all those of you who have been asking for kits and want to contribute to the further developement of this project. I’ll build up one or two boards as soon as they get here and do some testing. If everything works as planned I can order some more components and ship some kits soon after that.

EagleSnapshot

So today I’ll go through the changes I’ve made compared to the previous version. All in all the changes are quite minor and only require minimal changes in software. But let’s go through them one by one.

Non-volatile memory

This is the biggest change from a functional point of view. The PIC32MX250 doesn’t have any EEPROM memory of its own. So in order to be able to save some settings an the like I’ve added a Microchip 24AA16 I2C EEPROM providing 16kbit (2kB) of non-volatile memory. That should be more than enough to store any settings and calibrations one might want to make. For example, this gives the user the possibility to calibrate the filter kernels to the transducers used. Of course, the software first needs to make use of that memory but I think it’s great to have the possibility to durably  store a reasonably large amount of data.

dirtypcb_top

Support for 5V I2C communication

I’ve hinted at this in a previous post. On the Rev A board the I2C signals were pulled up to the 3.3 volt rail. This was great as long as one didn’t want to interface to a 5V device such as an Arduino. The microcontroller pins are 5V compatible so you want to be able to pull those lines up to 5V whenever you interface to a 5V device. So I’ve added a diode to allow the SDA and SCL lines to be pulled higher than 3.3 volts. The I2C reference voltage of 3.3 volts minus a schottky diode drop or about 3.1 volts is accessible from the I2C header at the bottom. So just connect that to the external device’s 5V operating voltage and you have a fully compliant 5V I2C bus.

dirtypcb_bottom

Physical layout and connectors

There was a rather large 8-pin connector on the last version to connect to the transducers. Now all the connectors along the edges of the board are standard 100-mil headers. This also allowed me to slightly shrink the physical size of the board to 60x70mm.

The pinout has also slightly changed. The board is now powered from a header on the right-upper side and all three voltage rails are now externally accessible from a newly added header on the right side. The pin order on the I2C and SPI headers on the bottom side of the board has changed, mainly to accomodate an exteral I2C reference voltage.

GerberViewerScreenshot1

Power supply

The tiny (SOT23-5) 3.3 volt linear regulator on the last version worked well but got rather hot when providing close to 50mA from a 12V input voltage. I never had any issues with it at room temperature but decided to be cautious and upgrade to a LD1117 regulator in a much larger SOT223 package. This should be more than sufficient any reasonable ambient temperature..

GerberViewerScreenshot3

Miscellaneous changes

I changed the digipot used to set the amplifier gain to a Microchip MCP4531. This model only has 128 steps but this is still more than sufficient for its task and it’s quite a bit cheaper than the 256-step version.

I also had to change the crystal because the model previously used became unavailable.

GerberViewerScreenshot2

That’s it for now. I’ll let you know as soon as the boards get here.

Ultrasonic Anemometer Part 25: I2C Interfacing and more

20160706_StandaloneAnemometer_023

It’s been a long six weeks since my last post but that doesn’t mean that I haven’t done anything since. Among other things, I wrote some code to get the I2C interface working and hooked the anemometer up to an Arduino Uno with an LCD display attached. Apart from demonstrating the I2C interface this also nice for testing. For the first time I can see what this thing is measuring in real time without hooking it up to a PC over USB.

20160710_StandaloneAnemometer_025

I2C Interfacing

But let’s look at the setup in some more detail. The PIC has a total of two I2C interfaces and I’ve made both of them accessible via the 100mil headers along the edges of the board. One of them is primarily intended for internal purposes like controlling the gain via the I2C digipot. The other one can then be used to interface to some external logic without having to worry about any internal communication. This external I2C interface also comes with 5V compatible pins which means we can interface to 5V devices like an Arduino without any further logic level translation. All we need is a pair of pull-up resistors pulling the SDA and SCL lines up to 5 volts. The Arduino’s Atmega328 already has built-in (weak) pull-up resistors so that’s not a problem. I didn’t think of interfacing to a 5V device when I designed the board and pulled the I2C signals to the anemometer’s 3.3V supply. So for proper 5V operation I’d have to unsolder the two 10k resistors. Luckily, 3.3 volts is enough to almost certainly be recognized as a logic high by the Arduino so the setup works anyway. But I will think about how to improve this in the next version. I might add a diode to allow the lines to be pulled higher than 3.3 volts.

The I2C interface can be configured to act as a master or as a slave device. For now I’ve only implemented slave mode so the wind meter behaves just like a I2C temperature sensor or external ADC. The Arduino acts as a master and asks the slave for its latest measurement every 250ms. The anemometer then returns 8 bytes of data consisting of 2 status bytes, north-south wind speed, east-west wind speed (both in mm/s as signed 16-bit integers) and temperature (0.01 degrees centigrade as signed 16-bit integer). It’s also possible to send data to the anemometer. That data is then saved to a buffer is otherwise ignored.  In a future version of the software one might use this functionality to set the data format to wind speed and wind direction or to change the temperature unit to kelvin or fahrenheit.

20160710_StandaloneAnemometer_027

It would not be difficult to implement master mode as well but so far I haven’t done it yet. A lot of code could be copied from the module communicating with the digipot where the PIC acts as a master. The anemometer could then push data to an external device whenever a new measurement becomes available. Definitely something I’d like to implement at some point but no priority right now.

Bug fixes

I’ve found (and fixed) a number of bugs while testing. Among other things, the axis and direction signals were not always properly set and so the measurements did not always correspond to the direction they should. So take with a grain of salt some of the test results reported earlier. Some of them were almost a bit too good to be true and this bug might have been the cause. I suspect I might have compared two successive measurements in the same direction when I actually wanted to compare measurements taken in opposite directions. But I’ve fixed the code and I’m confident that now all the directions are set and reported correctly.

Individual filter kernels

EqualAmplitudes

As can easily be seen from the scope screenshots above, the shape of the received signal varies quite a bit from transducer to transducer. Note that the amplifier gain is dynamically adjusted to make sure the peak amplitude is the same for all of them but the shape still differs quite substantially. So the kernel for the matched filter has to be some compromise to suit them all. I have now modified the software to use four individual kernels, one for each direction. This gives us the flexibility to calibrate the kernel to each transducer and so get more reliable results for the absolute phase.

Revised board

20160710_StandaloneAnemometer_026

My main priority at the moment is to complete the revised version of the board and to order a small series of boards. Until recently I never ordered a board from a professional board house so there are quite a few things for me to learn. For the first time I have to worry about silk screen, for example. Or solder mask. On the other hand, a board house can do a lot of things I can’t. Plated-through holes for example. Or smaller vias. Or place vias under an IC. This means a lot more flexibility in my layout that I first have to get used to. But I’m making progress. I got a simple design of mine produced by dirtypcbs.com and got a batch of very usable boards. More on that in a future post.

Wind tunnel

If you’ve been following this project for a long time already you might remember my simplistic wind tunnel made of a 120mm brushless fan and a cardboard tube. I got a number of suggestions from you guys on how to build something better than that and I also found some useful material online. So I’ve started to build a much improved wind tunnel that will hopefully allow me to perform more meaningful tests. That’s also for one or more future posts.

20160706_StandaloneAnemometer_021

Software ideas

I’ve also played around with some software ideas that I believe to have potential. One is dynamically adjusting the frequency. I’m now only working at the transducer’s nominal frequency of 40kHz. But the individual transducer’s resonance frequency might be somewhat different and might change with temperature or age. So I might try to adjust the frequency dynamically using some perturbation and observation algorithm.

I’m also thinking about measuring at two slightly different frequencies (say, 39.5kHz and 40.5kHz) and using the two phase shifts to figure out the absolute time-of-flight. I’ve given it a try and was not very successful but I haven’t given up on that idea yet.

So that’s it for now. The code for the Ultrasonic Anemometer as well as the Arduino are available for download. See the overview page for the respective links.

Ultrasonic Anemometer Part 24: New Microcontroller and Software Controlled Gain

It’s been almost three weeks since my last post and some further progress has been made. I’ve upgraded the microcontroller and can now control the gain of the second amplifier stage in software. But let’s look at the changes in some more detail.

20160625_AnemometerDriver_008

New microcontroller: PIC32MX250

I mentioned last time that I was running out of code space, i.e. flash memory on the PIC32MC220. That particular model has only 32k of flash and I’m using the free version of the XC32 compiler. That free version only performs limited amounts of optimization and therefore produces rather large and slow code compared to the standard and professional version. But the other two are rather costly (around USD 500 and 1000, respectively) and are not really an option here.  And at least so far I’ve always had all the speed I needed so the only issue was flash memory.

So the straight forward solution was to upgrade the microcontroller to an otherwise identical model with more memory. The 250 I’m using now has four times more of both flash (now 128k) and ram (now 32k). I unsoldered the old chip using a hot air gun and soldered in a PIC32MX250 in its place. Now we have all the flash we need to continue our journey.

20160625_AnemometerDriver_005

Getting I2C to work

My design uses a dual op amp for the necessary amplification of the received signal. The first stage is ground-referenced and uses a simple resistive divider to set the gain (currently set at 11).

The second stage is biased to 1.65V (i.e. half way between ground and 3.3 volts) and has its gain controlled by a I2C digipot. The idea is to have the PIC control the digipot so it can adjust the gain as needed to compensate for any effect that might change the amplitude of the received signal.

So in order to control our digipot we first need to get the I2C interface working on the PIC. There are two (identical) I2C peripherals on the PIC32MX2xx and this design uses one of them (#2) exclusively to communicate to the digipot. The other one (#1) is then available to interface to the outside world.

I’ve written a set of simple functions to use the I2C interface. So far I’ve only implemented  master transmit mode since that’s the only thing we need here.

  • void app_i2c_internal_init(void);
  • bool app_i2c_internal_write(uint8_t address, uint8_t *data, uint8_t length);
  • void app_i2c_internal_writeDigipot(uint8_t value);

The functions are entirely non-blocking so they can be called from within the interrupt service routines that do the measurement.

Fixing a design bug

Unfortunately, I made a mistake in the schematic when I referenced the feedback loop of the second op amp stage to ground instead of the mentioned 1.65 volts. Now let’s look at what that did to the signal. First, below is the signal with the digipot at 0 (of 256). The gain is set to one and all is well.

AmpError_Pot0

But at a setting of 64, some clipping starts to occur as shown below.

AmpError_Pot64

With the gain turned up to 4 with a digipot setting of 192 things get really uggly with a completely unrecognizable output signal.

AmpError_Pot192

I was extremely lucky that the correct signal was available right next to the pin where I needed it so all I had to do was to cut the ground connection with a carving knife and to connect the correct signal using a tiny bit of wire.  The change is hardly visible on the photo below. Can you spot it? It’s almost at the center of the photo at pin 5 (bottom right) of the small 8-pin IC. It now connects to the 10k resistor at the bottom instead of the ground plane on its right.

20160626_AnemometerDriver_009

Controlling the gain in software

With that error fixed we can now set the gain as we please. Here some examples. Yellow is the (unamplified) input signal whereas green is the output of the (fixed) first amplifier stage.  The purple signal is calculated as the difference between the DAC+ and DAC- signals. Those two signals is what we feed into the PIC to be measured.

With the gain at 1 (digipot at 0) we get a about 2.6 volts peak-peak. A bit little but sufficient.

Amplifier_Pot0

This is probably about what we want. Digipot at 50 resulting in a peak-peak amplitude of a bit above 3 volts.

Amplifier_Pot50

Needless to say that we can jerk the gain up far more than that if we want.  Below is an example where a gain of 4 results in a seriously clipped signal.

Amplifier_Pot192

Now let’s look a a situation where such a high gain might actually be useful. In the example below I’ve lowered the input voltage to only 5 volts. As a result the received signal is only 90mV in amplitude instead of the 220mV seen above. That’s easily compensated with a bit of help from our digipot. With a setting of 170 we get a nice, clean 3 volt output signal.

Amplifier_Vin5_Pot170

Calculating the necessary gain

For the examples above I’ve explicitly set the gain in the code. But what we really want is to have the software calculate and set the necessary gain automatically.

So I’ve implemented a simple algorithm to do so. I set some target value for the peak-to-peak amplitude of the signal measured by the DAC. Knowing the amplitude and digipot setting of the last measurement I calculate the optimal gain and digipot setting for the next measurement.

AmplitudesGain1

Above is the signal without this automatic gain setting in place (gain=1). As you can see, the peak-to-peak amplitudes vary quite a bit between transducer pairs. So the gain calculations, too, work on a transducer pair basis. Each direction gets its own setting to compensate for different transducer sensitivities.

Below is a screenshot of the result. Notice how now all transducer pairs have identical (and somewhat larger) amplitudes.

EqualAmplitudes

That’s it for today. Click here for my next post.

Ultrasonic Anemometer Part 23: First successful measurements

In my last post I was happy to report that I managed to get the USB interface to work. This interface has since proved to be extremely valuable in software development and testing. While the device is taking measurements you can look at the results (or intermediate results) at your PC in real time. You can even log large amounts of data to a .csv file and inspect the results in Excel.

20160514_StandaloneAnemometer_049

But let’s look at the developements in a bit more detail. Last time the device was sending pulses and capturing some of the resulting zero-crossings but not much more than that. And in a not very structured way.

Measuring amplitude

Now it’s not only capturing the times when the zero-crossings occur but also measures the amplitude of the half-waves in between. In order to do that an interrupt is generated at every zero-crossing capture. Inside that interrupt service routine time of the zero-crossing is saved (as previously) and the ADC is started. In order to measure the amplitude at its maximum (or minimum in case of a negative half-wave), the ADC first samples the input for a 6.25 microseconds (which corresponds to a quarter-wave at 40kHz). I’ve chosen the sampling period slightly shorter to compensate for the time from when the zero-crossing occured unil the ADC is started. After the sampling period the internal sample-and-hold amplifier switches from sample to hold and the actual analog-to-digital conversion takes place.

The PIC’s datasheet advertises a maximum sampling rate of 1 million samples per second. Beware, though, that this requires separate Vref+ and Vref- connections and doesn’t work in differential mode anyway. In my configuration without separate analog references and differential measurements the maximum sampling rate drops to 400k samples per second. And the reported conversion time doesn’t include the sampling time yet. I wasn’t aware of that but luckily the PIC’s ADC is still more than fast enough for what I’m doing here.

20160428_StandaloneAnemometer_028

Finding the absolute phase

Capturing the zero-crossings is relatively easy and the results are very precise. But there’s a challenge: there are hundreds of zero crossings for each burst of pulses sent. How do you know which zero-crossings to use?

The Arduino-based anemometer used an analog envelope detector to solve this problem. After averaging plenty of samples this works most of the time. But even after many attempts to tweek the analog circuit the envelope detector approach was never as reliable as I wanted it to be.

My standalone anemometer has an entirely different approach. Being able to digitally measure the amplitude of each half-wave we can tackle the challenge in software. We have a series of measurements and we have an idea of that the signal we are looking for looks like. Looking the scope screenshot below you could easily and reliably determine where the maximum amplitude occurs. That’s what we need to teach the software to do.

ZCD_Capture

One could, of course, just loop through the all the amplitudes and find the maximum. But there are a lot of similar amplitudes and so any noise would make the result unreliable.

I did a bit of reading on digital signal processing (DSP) and realized that this is a classic DSP problem. The solution is a matched filter. You take the signal you are looking for (also known as kernel or template) and multiply it sample by sample with the captured signal and sum up the results. Do that for every possible (or reasonable) overlap of signal and kernel and find the position where the result reaches its maximum.

If you’re new to the subject (like I am), Steven W. Smith’s Digital Signal Processing is a great place to start. It’s even available online for free at dspguide.com. The method is also referred as convolution or correlation depending on who you ask. This wikipedia article gives a nice introduction to the subject.

Implementing the matched filter

I’ve parameterized the software to capture 34 zero crossings as well as the n=33 amplitudes in between them. The kernel consists of k=17 data points with a peak in the middle.

There are n-k+1=17 possibilities to entirely overlap the kernel with the signal. We can skip all the odd numbers where a negative kernel value would be multiplied with a positive sample value and vice versa. This leaves us with 9 possible positions to chose from.

For each position we need to do 17 multiplications and 16 additions. And we need to do that for every single measurement, i.e. 512 times per second. That sounds like a lot of work and it probably is. Luckily, since this is a very common DSP task, microcontrollers like the PIC32 have a special instruction for this. It’s called MAC – Multiply Accumulate and executes in a single clock cycle. The result is just amazing. The corresponding ISR takes less than 30 microseconds, including some housekeeping tasks as can be seen in the screenshot below.

Once the maximum amplitude is found, the 16 zero-crossings around it (i.e. the 8 before and the 8 after) are summed up and the sum is saved as the result of that measurement.

ConvolutionTime

Averaging measurements

The goal is to report wind speed and temperature four times per second. Since there are 512 measurements per second we can use up to 128 individual measurements  (32 in each direction) to do our calculations.

So we have plenty of data to identify and exclude outliers and do some averaging of the remaining samples. Robust statistics is the key word here, click here for the corresponding wikipedia article.

For now I’m doing something reasonably simple: First the 32 results per direction are sorted. The 12 lowest and 12 highest results are ignored and only the 8 results in the middle are summed up.

This might seem wasteful but it makes the average very robust against outliers and still results in 16 x 8 = 128 zero-crossings being averaged. The resolution of each zero-crossing is equal to 1 / 48MHz or about 20.83 nanoseconds. Summing up 128 of them gives us a resolution of far below a nanosecond. Beware however that resolution doesn not imply accuracy.

As a last step, the resulting sum is multiplied with 125 and then divided by 768 to make the unit 1 nanosecond. So every 250 milliseconds we finally have 4 time-of-flight measurements with a resolution of 1 nanosecond. That’s what we will use to calculate the winds speed as well as wind direction and temperature.

This sorting and averaging step is a bit more costly in terms of computation time. It takes around 600 microseconds to complete (see below). But unlike the convolution, it only has to be performed 4 times per second so this is more than fine.

CalculatingAverageToF

Calculating wind speed

We are finally in a position to calculate the actual wind speed. There are various ways of doing this. For now I’ve just done the simple approach of taking the difference in time-of-flight, assuming room temperature and solving for wind speed. This means solving a quadratic equation for which I have resorted to floating point math and using <math.h> for the square root function.

I don’t have my wind tunnel any more so doing any testing was difficult. But one thing was soon obvious: at zero wind speed, the time-of-flight measurements match extremely well and are extrordinary stable from measurement to measurement. Also, looking at the intermediate results (i.e. the 128 single measurements before averaging) shows that there are basically no outliers present. I could randomly pick a measurement and still get a very reasonable value.

Something seems to be systematically wrong with the first of the 128 measurements but I haven’t had time to look into this. Otherwise, the results are impressively stable. And I’m only using a relatively primitive kernel for the matched filter.

USB data logging

As I’ve mentioned at the beginning, the USB interface lets me do some serious data logging even at this early stage of development. Here’s a list of what I can do by sending a character to the device from a USB terminal.

  • ‘Z’: Get all the 34 zero-crossings of a single measurement
  • ‘A’: Get all 33 amplitudes of a single measurement
  • ‘F’: Get a full single measurement, i.e. 34 zero-crossings plus 33 amplitudes
  • ‘T’: Get all the 4 x 32 = 128 time-of-flights for a measurement series
  • ‘R’: Get the 4 averaged time-of-flights as well as wind speed and temperature

Some versions of these commands also let you specify the direction you’re interested in as well as how many sets of data you want to receive. This makes it easy to log large amounts of data that you can use to test possible algorithms on your PC before you implement them on the PIC.

20160428_StandaloneAnemometer_025

Next steps

The next step is to get the I2C digipot working so I can control the amplification in software. My idea is to aim for a maximum amplitude of around 3 volts independent of wind speed and so on.

There’s also plenty of work to do to improve the algorithm that calculates wind speed and temperature. And then I also have to implement the I2C and SPI interfaces that let the anemometer communicate to other embedded devices like an Arduino or Raspberry Pi.

Having used floating point math and <math.h> I’m also running out of flash memory. I’m currently using 93% of flash (32kB total) and 52% of ram (8kB total). There will be a slightly revised board (Rev B) that uses a PIC32MX250 which is identical to the MX220 used here but has four times as much flash and ram.

That’s it for today. On the overview page you find the software at the stage of developement just described as well as some examples of logged data (all at zero wind speed) as .csv files.

The next post on this project is here: Part 24.

Ultrasonic Anemometer Part 22: USB up and running

20160514_StandaloneAnemometer_047

Last time I showed you the nice new hardware of the new standalone ultrasonic anemometer. But at that time I had hardly any software written for it so I couldn’t do much with its 32 bit microcontroller. So the last two or three weeks I spend lots of time writing code that I’d like to share with you today.

20160428_StandaloneAnemometer_039

As I mentioned last time I worried most about getting the USB working properly. All the bit-fiddling with timers, PWM modules, input capture modules and the like can be time consuming and at time even frustrating. But I have all the confidence that I finally get what I aimed for. It might take a few nights digging through data sheets but in the end it will almost definitely work.

Now with USB I don’t quite have that level of confidence. The USB specification is quite overwhelming and there are almost countless registers to properly set and USB descriptors to specify. Without some sample code I’m pretty much lost I must admit.

Outdated Application Notes

So I was happy to see that Microchip has published a number of application notes and accompanying software. A handful of .h and .c files that you can include in your project, change some settings to suit your particular application and you’re ready to go. At least that’s what I thought.

20160514_StandaloneAnemometer_048

All those application notes were published around 2008 and the code they are referring to is nowhere to be found nowadays. Aparently, Microchip has been quite pushy trying to get their PIC32 customers migrate to their new library or rather framework called Harmony. MPLAB no longer even includes the the PLIB peripheral library everyone has been using for decades. Microchip has depreciated it and while you can still download it they make very clear that Harmony is the library of the future.

On the road with MPLAB Harmony

So faced with few other alternatives I turned to the aforementioned Harmony library. It’s many hundred megabytes to download and takes up almost two gigabytes of disk space. It integrates nicely into MPLAB X and so I created a first project. You can graphically configure the clock and pin settings so I did that. Clicked on ‘create code’ and was nothing less than shocked by the code I got.  Around a dozend source files scattered around in a dozend deeply nested folders and subfolders. And that was not yet it. Those files referenced dozends more files that came as part of harmony. It took me quite a while to just more or less figure out what the project was doing. And it didn’t do anything yet except setting some clock and port settings. Just a nightmare.

20160514_StandaloneAnemometer_045

But I felt I had few other options so I continued with harmony anyway. And after a few hours I had an almost working USB device. It enumerated just fine but I couldn’t send any data from or to it. It took me more than a week to finally get it working. In the end it was just a single character: a 1 instead of a 2 in one of the configuration files. But until then I had spent a lot of time with that harmony code and was forced to read through a lot of its documentation which is worth thousands uppon thousands of pages of PDFs.

So USB was finally working and I had acquired some rudimentary understanding of MPLAB Harmony. I still hated the whole framework but I thought that I now understand it well enough to change the code to suit my taste. So I spent another few nights trying to do that until it dawned on me that this is leading nowhere. Fortunately, by that time I had read and learned some more about Harmony and was willing to give it another try working with it as opposed to against it. So for the last week or so I was doing that.

And while I still don’t love everyting about it I now feel comfortable working with that library. I’ve tried to follow its conventions and recommendations even where they didn’t make too much sense to me. I think this will make it much easier for others to work with my code and chances are that the conventions and recommendations will start to make more sense as I get more familiar with the framework. And its nice to know that I can integrate another module or migrate to another microcontroller without having to re-invent everything.

20160428_StandaloneAnemometer_037

That was a long introduction but after all that pain the last few weeks I just had to write that down. Now to business.

USB

The anemometer inplements the USB HID (human interface device) class. While mainly aimed at devices such as mice and keyboards, this USB device sees a lot of use in applications that have nothing to do with human interaction. You can transfer up to 64 kbytes of data (64 bytes every millisecond) in each direction with guaranteed latency and that’s plenty for many applications. That data can be anything and in any format you like. And the good thing is that you don’t need to write your own drivers. You can free-ride on the device drivers that came with the operating system just like you don’t need to install a driver when you attach a new keyboard to your computer.

I’ve written a bit of code on top of the Harmony library to provide the main application with a simple to use interface. It nicely encapsulates the the USB logic and data and adds a bit of buffering functionality on top of that. It’s just two files: app_usb.h and app_usb.c in the app_usb subfolder. You can basically include them in your project and they handle all the USB stuff for you.

Once initialized, the necessary code runns inside an interrupt service routine (ISR) so the main application has nothing to worry about. All the USB data including the data buffers are private to the app_usb.c file. The app_usb.h defines just 8 simple, non-blocking functions:

  • void app_usb_init(void);
  • bool app_usb_isReady(void);
  • uint8_t* app_usb_getReceiveBuffer(void);
  • uint8_t app_usb_getReceiveBufferCount(void);
  • uint8_t* app_usb_getTransmitBuffer(void);
  • uint8_t app_usb_getTransmitBufferCount(void);
  • void app_usb_freeReceiveBuffer(void);
  • void app_usb_sendTransmitBuffer(void);

At system initialization you call app_usb_init(). But you can’t expect to have an USB connection. You need to check app_usb_isReady() every time. You never know if there is a USB cable plugged in and even if you had a connection that cable might be unplugged at any time. The module handles all that hot plugging/unplugging gracefully.

20160428_StandaloneAnemometer_038

The module implements a circular buffer for both received packages (aka frames) and packages to be sent. Each package is 64 bytes in size and the ring buffers can be set at compile time but must be a power of 2. Currently the receive buffer consists of 4 frames and the transmit buffer of 8 frames.

The app_usb_getReceiveBufferCount() informs the caller how many new USB frames have been received if any. app_usb_getReceiveBuffer() then returns a handle to the first received frame. FIFO – First In First Out. Once the frame is no longer needed the application calls app_usb_freeReceiveBuffer() and the buffer can be re-used.

Sending data works similarly simple. app_usb_getTransmitBufferCount() returns the number of transmit buffers currently in use. If this  number is smaller than then number of buffers then more data transmissions can be scheduled. Get a handle to then next buffer by calling app_usb_getTransmitBuffer(), write the data to be sent to the buffer and then call app_usb_sendTransmitBuffer().

Taking measurements

This way the main application can focus on more interesting tasks such as measuring wind speed and direction. The first task for doing so is sending some pulses.

SendReceiveOverview

If you’ve alredy followed this project for a while you are probably familiar with the kind of screenshot above. Bursts of a PWM signal at 40kHz (SIGNAL, red) are sent trough ultrasonic transducers. Note that the signal is now active-low (i.e. inverted) since the mosfet drivers used to drive the transducers are themselfs inverting.

As before, the DIRECTION  and AXIS signal determine which transducer is transmitting and receving. The SCLK and MOSI signals are just used for debugging purposes for now. They indicate when an ISR is running which helps a lot since in this example ALL the code is running inside an ISR.

TimeZero

As you can see above, the AXIS and DIRECTION signals are set a few microseconds before we start sending pulses. The first edge of the burst occurs when the 32 bit-timer (consisting of the 16-bit timers TMR2 and TMR3) is cleared. This is the point in time all our measurements are referenced to. The timer runs at the full CPU clock of 48Mhz so the resolution is about 20.8 nanoseconds.

SendReceivePWM

Output Compare module 2 (OC2) handles the PWM pulses. Currently each burst consists of 12 pulses. At 48MHz a full wave equals 1200 clock cycles as opposed to 400 on the Arduino previously used. Note that the frequency of the pulses is precisely 40kHz as expected and that very little time is spend in ISRs despite the unoptimized C code.

SendReceiveCloseUpWithComments

The timer overflows precisely 512 times per second so each measurement takes a little less than 2 milliseconds. Output compare module 1 (OC1) can be used to generate an interrupt at any time during that interval. Currently it does three things: It takes care of the AXIS and DIRECTION signals which are set just shortly before the timer overflows. It also triggers the measurements of the zero-crossings and cancels them if they don’t complete withing reasonable time.

ZCD_Capture

The input capture module 4 (IC4) captures the zero-crossings of the received and amplified signal. The board contains a high speed comparator to detect these events so the microcontroller only needs to measure the precise time whn they occur. The IC4 module has an internal buffer so we don’t need to generate an interrupt after every capture. I’ve currently set things up so that an interrupt occurs after 4 values have been captured up to a total of 32 measurements.

Commicating the results over USB

In order to find the wind speed and direction we need to somehow identify the peak in the received signal which is where the ADC comes in. But I think this is enough for now and I instead show you how the measurements taken so far can be sent to a PC via USB.

The board doesn’t have any display or anything to communicate with a human. There’s not even a status LED we could blink. That would have been nice but there are just not enough I/O pins unfortunately. So connecting the board to a PC via USB is a straight-forward way of communicating with this device.  Actually, USB will be the main testing and debugging tool during development. The in-circuit debugger isn’t very useful in this application since the measurements have to happen in real time.  Setting breakpoints will just mess things up.That’s why I insisted to get USB working first.

I’ve downloaded a simple terminal called YAT (yet another termial) that allows me to easily talk to the anemometer board. The anemometer looks at the first character in any message sent from the host and then decides what to do. So far I’ve implemented:

  • ‘T’: Return the current value of the 32-bit timer
  • ‘Z’: Return axis (0/1), direction(0/1), measurement count (0-31) values as well as the results of the first 9 zero-crossing measurements
  • Anything else: Echo back the received string

Here’s what my chat with the Standalone Ultrasonic Anemomete looked like.

YAT_Capture

Of course, the measurement is far from complete at this stage but I think this is a nice foundation to build uppon. The code is quite clean for this early stage of development and being able to communicate with the device in real-time over USB is really a great advantage.

The software can be downloaded on the project over view page and as always I appreciate any feedback.

The next post on this project can be found here.

Ultrasonic Anemometer Part 21: Standalone Anemometer Hardware

Last time I went through the design of my new standalone anemometer. Now it’s time to build this thing and see if it works as planned.

20160428_StandaloneAnemometer_034

After I fried a couple of chips on my driver circuit testing board due to a wrong chip in the power supply I was a bit more careful this time and built up the board step by step.

20160426_StandaloneAnemometer_016

Only after I confirmed that the power supply was ok I dared to solder some more.

20160426_StandaloneAnemometer_019

The next step was to add the PIC32 with the crystal, the programming header and all the caps they need. This is a chip family that I’ve never used before so I wanted to first see if I can program it. All went well and I managed to get it to run on the crystal’s 8MHz boosted up to 40MHz by an internal PLL. So I was ready for the rest.

20160428_StandaloneAnemometer_031

I wrote some very basic software and confirmed that at least the basics were working ok. I was able to send and receive pulses, the pulses got amplified, the zero-crossing detector worked and so forth.

20160428_StandaloneAnemometer_024

As mentioned, I’m entirely new to the PIC32 microcontroller series. There are a lot of similarities to the PIC16 and PIC18 series that I’m quite familiar with but still it’s always a challenge to work with a new family of chips and the tools that come with it. I took me the better part of an afternoon to master the vectored interrupts with the different priority levels and so on.

20160428_StandaloneAnemometer_040
Driver circuit (front) and standalone anemometer (back) side by side.

By the way, with this project I’m using the free MPLAB X IDE with the also free XC32 C compiler from Microchip. So anyone is able to write, modify or compile code for this thing with free software. At least at the moment you need a programmer to actually burn the chip. But the PICkit3 only costs around 50 dollars and my idea is to write a USB bootloader so that any user can modify the software of a pre-programmed board.

20160514_StandaloneAnemometer_050

So now comes what I think might be the hardest part: Getting the USB to work. I’ve spent quite a few hours so far but haven’t managed to get it working properly yet. If anyone has experience with this kind of software development – Let me know, any help is highly appreciated.

It now works: Click here to view it.