If you’ve read my last post you’re already familiar with my Inductance Meter project: https://soldernerd.com/2015/01/14/stand-alone-inductance-meter/. At that time the hardware was ready but there was no software yet. That’s been corrected, the inductance meter is now fully functional.
From a high-level point of view the new software is very similar to the Arduino sketch I wrote for the Inductance Meter Shield (https://soldernerd.com/2014/12/14/arduino-based-inductance-meter/). If you look a bit closer, you’ll notice some differences for several reasons:
This project uses an entirely different microcontroller: A PIC 16F1936* instead of the Atmel Atmega328
This code is written in C (for the MikroC for PIC compiler by Mikroelektronika), not Arduino-style C++
The display I’m using here comes with a I2C interface rather than the familiar Hitachi interface
*: In an earlier version I wrote 16F1932 instead of 1936. Thanks to Ralph Doncaster for pointing this out to me. By the way, Ralph has his own blog at http://nerdralph.blogspot.ca which I regularly enjoy reading.
I’ve noticed that the MikroC compiler is not very popular among hobbyists but I like using it. It’s entirely free as long as your compiled code does not exceed a certain size. You can use any features, any library, just anything for free when you’re getting started. Yes, once your projects get bigger you’ll run into the limit and will have to buy a license but it was worth the price to me.
As expected, the I2C display took me some time to get used to. It’s a MIDAS MCCOG21605B6W-BNMLWI (Farnell: 2063209). It seems to use a Hitachi-compatible controller with a I2C interface built on top of it. I like the concept and will probabely use one of these again. If there is a downside, it’s the data sheet. I had to do quite a bit of guessing and trial-and-error to get it working. I had used Hitachi-compatible MIDAS displays before so I had some idea what might work otherwise I might have given up. Examples?
No page numbering. Yes, this is a minor thing but have you ever seen a data sheet without page numbers?
There is a reset pin. As many reset pins, it’s active-low. But the data sheet never says so. Not explicitly, not with a bar accross the RST, not with a ~RST. Absolutely nothing.
It explains some of the Hitachi-functionality in great detail but does not really tell you that this functionality is not accessible over the I2C interface.
Like Hitachi-compatible types, this display needs some start-up time before you configure it. Otherwise it will just not work. But the data sheet doesn’t mention that with a single word.
But it’s all working fine now. The PIC16F1936 has more than enough ROM, RAM and processing power for this meter so don’t expect the code to be optimized in any way. It was just not necessary. It does most of the math in floating-point which bloats the (compiled) code size and is dead-slow on this kind of architecture but it’s still more than fast enough and only uses around half of the available RAM and ROM.
I think the code itself is quite readable but don’t hesitate to ask if you have any questions. Here’s the code as zip: LMeter
The arduino-based meter works well and made a great proof-of-concept. But for everyday use you’re probabely not looking for an arduino solution but rather something that looks and feels more like a multimeter. That’s why I’m following up with this stand-alone version.
So this version is battery powered and comes complete with a 3D-printed case. It uses a mid-range PIC microcontroller, a PIC16F1936. Not that there’s much special about this model, I just happened to have some left from previous projects. I also thought about using a Atmel Atmega328, the same chip that is on the Arduino UNO.
Using an entirely different chip means I’ll have to write the software from scratch. But I felt that the Atmega328 was just too much of an overkill just to measure a frequency and control an LCD. They are quite a bit more expensive than the PIC, CHF 3.70 compared to CHF 1.90 @10pieces at Farnell where I get just about all my chips.
Talking of the LCD: The one I’m using here comes with a I2C interface. It’s blue with a white backlight and 2×16 characters and really tiny. I bought 2 of them years ago because they were small and relatively cheap (around 15CHF) and don’t require so many precious I/O pins of your microcontroller. Somehow I never used them but here their small size makes them a good choice. I/O pins aren’t a constraint here obviously as most of the 28 pins are unused.
I’m not yet familiar with the details of how they are controlled. I had a look at the data sheet and it looks like you send them just about the same commands like with the standard Hitachi compatible ones, just over I2C. But I expect to spend an evening or two figuring out the details.
The case was designed using FreeCAD. As the name suggest, it’s a free (and open-source) CAD design tool. This was only the second time I was using it but I found it quite easy to learn.
I printed the case at the Zürich fab lab (zurich.fablab.ch) on one of their Ultimakers. Was my first 3D-printing project, thank you very much for your support, everyone.
As always, I’ll put all the files online as a zip. So you can download all the Eagles plus PDFs as well as the FreeCAD models. Here it is: InductanceMeter. I haven’t written any software yet but I’ll but that online, too, as soon as it’s finished.
It’s been a while since the last post of this series. As so often, the task turned out to be more demanding than I first thought. And then I was also entirely new to assembly language, got distracted by my Inductance Meter Project (https://soldernerd.com/2014/12/14/arduino-based-inductance-meter/) and went on a skiing holiday. But finally, the promised library is ready.
If you’re new to my Arduino-based ultrasonic wind meter project, you might want to click here for an overview: https://soldernerd.com/arduino-ultrasonic-anemometer/. This is also where you find all the various downloads, including the new library.
I’ll go through the meaning and usage of these one by one:
anemometerSetup()
This function has to be called once before the anemometer can be used:
void setup() { anemometerSetup(); }
anemometerStatus
anemometerStatus notifies your when a new set of measurements has been completed and is ready to use. Every time a new set is ready, anemometerStatus will be set to 1. You have to set it back to 0 once you’re done with your calculations.
if(anemometerStatus==1) { /* use the results */ anemometerStatus = 0; }
A new set of results is made available exactly every 250ms or 4 times a second.
anemometerCalculationSet
anemometerCalculationSet is a pointer to the ready-to-use results.
Internally, the library uses a ping-pong buffer. Every time a new set of results becomes available, anemometerCalculationSet is updated to point to the last completed set of results.
The result set pointed to by anemometerCalculationSet is a
This is easy to understand. It is the time it takes for the envelope detector to trigger. It is averaged over 32 measurements. Reference point is the rising edge of the first pulse sent. The unit is nanoseconds (ns).
sine, cosine
sine and cosine indicate the phase shift between the transmitted and the received signal.
This was by far the most challenging part of library. For each of the 32 measurements, 4 rising and 4 falling edged of the zero-crossing detector are evaluated. So the phase shift indicated by sine and cosine is an average over 256 measurements. But phase shifts wrap around, casually speaking. A phase shift of 359 degrees is almost the same as phase shift of 1 degree. The average should be zero degrees, not 180. Now you could try to solve the problem by constraining your phase shift to the range of -180 to +180 degrees. But that only moves the problem to the other side of the circle: -179 and +179 degrees are almost the same and their average should be 180 degrees, not zero.
I spent quite some time thinking about this and came up with increasingly complex alogrithms that still failed when some unlucky combination of angles was encountered. Remember, we are trying to average n (currently n=256) angles, not only 2.
But of course, many people smarter than me have encountered the problem before and have come up with a perfectly elegant solution: If phi is your phase shift, then calculate sine(phi) and cosine(phi). Sum up all the sines and all the cosines. Then use the arctangent function to convert the summed sines and cosines back to an (averaged) angle. [If wordpress had something like LATEX support, one could state this as a nice-looking formula]
So there is an elegant solution. But there’s also a tight time budget: The zero-crossing detector triggeres every 12.5 microseconds (us). In order to not miss the next zero crossing, we need to calculate both sine and cosine and add them to their respective sum in less time than that. And then there is some overhead like context switching. Plus we also have to do some housekeeping during that time.
sine and cosine are expensive functions, even more so on a 8bit microcontroller. So the only way was using a lookup table. With this approach I managed to stay within budget (around 10.8us). Besides, missing the next edge is not the end of the world – the edge after that is almost as good.
So we now have summed sine and cosine values – how do we use them?
atan2(cosine, .sine) gives you the phase shift in radians. Multiply this by (180/PI) if you prefer degrees. My preferred approach is:
(12500.0/PI) * atan2(cosine, sine)
This also gives you the phase shift but with nanoseconds (ns) as unit which makes it directly comparable to the timeOfFlight which is also in ns.
temperature()
There’s also another function returning the temperature. It returns a int16_t containing the temperature in 0.01 degrees centigrade. So if the temperature is 23.45 degrees, it will return 2345.
It’s currently implemented using floating-point math and does not account for the sensor’s (slightly) non-linear nature. It’s there mainly as a placeholder. I’m planning to implement it properly using a lookup-table with interpolation which will make it much faster and will allow it to include the non-linear effects.
Arduino Sketch
The .zip file with the library also includes a very basic arduino sketch using that library. I’m still evaluating what is the best way to calculate the actual wind speed and direction taking into account issues such as calibration and the like.
But my preliminary results look quite promising and I’m confident that most if not all the heavy lifting has been done.