Tag Archives: software

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 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.

Temperature Controlled Fan

20160409_FanController_020

I’m currently mainly working on my new anemometer design but once in a while I get distracted. For example when my Keysight E3645A lab power supply was making so much noise that I could hardly concentrate. That’s when the idea of this fan controller was born.

20160403_FanController_012

Of course, the best temperature controlled fan in the world doesn’t help if you really need the cooling the fan is providing. But very often a small fraction of the cooling would do just fine most of the time. In my case the supply does control the speed of the fan. But it doesn’t seem to measure the temperature at all but seems to calculate the necessary cooling in a worst-case condition. An for a supply that may be rack-mounted together with lots of other heat dissipating gear the worst-case might be quite demanding. But my supply just sits on a shelf at, say, 22 degrees ambiant. And most of the time I’m hardly pulling any current. When working with microcontroller designs it’s rare for me to pull more than a few tens of milliamps. So little cooling is needed most of the time. But the E3645A (this one here does a better job) ran its fan at crazy speeds while the case still had this cold metallic feel to it.  So we can definitely do better.

20160314_FanController_017

So the first step was to open the supply and to see what kind of fan it uses and how it is controlled. After beaking some seals and opening the case I found a 60x60x25mm 12V fan of Chinese origin. I also found out that the supply uses linear control. So there’s no PWM or anything but the supply voltage just varies in (I think) four steps from 7.4 to 12 volts. Most surprisingly, this voltage is not ground-referenced but symmetric around ground, i.e. plus/minus 6 volts.

20160314_FanController_014

I was pleased to see that the fan connects to the main board by means of a standard two-pin 100mil header. So I could just plug anything in between the board and the fan.

20160313_FanController_005

That’s exactly what my first idea was. Stick with the original fan and just put a PWM controller in between. I’ve just recently made some LED dimmers and the technology needed here seemed to be very similar. So Rev A of my fan controller was born.

20160313_FanController_003

It’s simple: A linear 5V regulator, a PIC16F18325 microcontroller, an LMT86 temperature sensor and a N-channel mosfet. The PIC chosen here runs at up to 32MHz on an internal oscillator, has an internal voltage reference (of 1.024, 2.048 or 4.096V), six PWM modules and plenty of other nice features while comming in a small 14-pin package. So all I need to do is to measure the temperature, calculate the desired fan speed and set the PWM duty cycle accordingly.

20160313_FanController_008

My first surprise came when I first wanted to program the PIC. My trusted Mikroelectronika MikroC for PIC compiler doesn’t know that chip. And neither does my MikroProg programmer. So after a little bit of research I ordered a PICkit3 and downloaded the MPLAB IDE. As a nice side effect I can now also compile code for and program the fancier PICs like the DSPics and PIC32s. I might do that before long.

20160313_FanController_001

So I did the necessary programming (and debugging) and attached a small fan. It all worked but I had to chose a quite low PWM frequency in order to make the fan spin at lower duty cycles. And probably as a result of the low PWM frequency the motion of the fan didn’t look or sound very smooth.

20160313_FanController_006

With the larger fan from the supply things only got worse. I had to lower the PWM frequency even more into the tens of Hz range so it would spin at all. And even like this I couldn’t get it to run at low duty cycles. Of course, the low frequency caused nasty vibrations so I gave up on this approach. I read online that other people successfully use PWM on their fans but at least this model wasn’t happy to be PWM-dimmed. Does anybody know more about this? Was this an option in the old days before brushless motors were the norm? Is it that brushless motors aren’t unsuitable for this kind of control alltogether or does it depend on the specific model? Please use the comments section below if you can shead some light on this.

20160403_FanController_002

But I don’t give up easily so after a bit of research I ordered a four-wire fan conforming to the so-called Intel spec. Besides ground and +12V they have two control lines. A PWM line that lets you control the fan speed by means of a (25kHz nominally) PWM singal. And a so-called TACH singal that allows you to read the current fan speed. The PWM line has internal pull-ups to (depending on the fan) 3.3 or 5 volts so you just need to pull it low. The TACH signal needs an external pull-up resistor and gets pulled low by the fan twice per revolution. So you’re getting a digital signal with a frequency of twice the fan speed.

20160403_FanController_009

I ordered a EBM Papst 622/2 HHP which is the right size and somewhat more powerful than the original fan. The new board has a somewhat odd shape so I can use one of the fan’s mounting screws to mount the board as well. Note that all the copper has been removed around the mounting hole. The fan is attached to a heat sink which is grounded while our board runs on a split suply. So ground as our board sees it is not actually ground but a negative voltage so we have to be careful.

20160403_FanController_012

The new Rev B design runs on 3.3 volts in order to be compatible with any fan independent of the fan’s internal logic voltage. I’ve also used a different temperature sensor – a classic LM35.

20160409_FanController_018

Like the Rev A there is an LED to visually indicate what’s going on. There are also three pins on the microcontroller that are intended to be used as debug pins so I put some vias there to make it easy to connect a scope probe.

fancontrol_tach

Above you get an idea of what the TACH signal looks like. It’s a quite low frequency singal since there are only two pulses per rotation. So the measured 104Hz shown on the screenshot correspond to 3120 RPM.

fancontrol_37_degrees

Having a TACH signal to measure and three debug outputs to worry about made the software development somewhat more involved but it was well worth it. I’ve used the debug pins as follows:

  • Actual (i.e. mesured) fan speed. 100% corresponds to 10000RPM
  • Target fan speed. 100% corresponds to 10000RPM
  • Measured temperature. 100% corresponds to 100 degrees centigrade

So from the duty cycle measured by a scope you can easily read the speeds and temperature.

fancontrol_50_degrees

Of course this is only possible since there are some unused PWM modules left. But as I said, this PIC has 6 of them and only two are needed to measure the fan speed and another to control the fan.

fancontrol_27_degrees

The transfer function from temperature to fan speed can be freely defined in software. In the screenshots above the fan was running at 1500RPM up to a temperature of 30 degrees. Above that the speed would rise linearly until reaching its maximum of 9000RPM at a temperature of 55 degrees.

20160409_FanController_023

One could easily implement a PID control if one was so inclined but the slowly chaning nature of the temperature in such a setting makes this largely unnecessary so at least for now only the proportional part is taken into consideration when calculating the PWM frequency.

20160409_FanController_026

As you can see, the little board is nicely held in place by one of the fan mouning screws. By the way, the LED blinks roughtly once per second and its duty cycle corresponds to the target fan speed relative to the maximum fan speed of 9000RPM. So if the LED is on one-third of the time the target fan speed is one-third of the maximum speed or 3000RPM.

20160409_FanController_027

Unfortunately for my application, the supply senses the current consumed by the fan and shuts down if not enough current flows. This is probably a good idea and prevents the supply from possible damage if the fan is unplugged for example.

20160409_FanController_022

I found that it is possible to run the fan at a lower speed without the supply complaining but with fan speeds below about 4000RPM the there were conditions causing an error condition. So I ended up connecting a 1W 150ohms resistor in parallel to keep the supply happy even with the fan running at only 1500RPM.

20160409_FanController_025

I believe that my settings are very much on the safe side. At a a temperature of around 50 degrees measured inside the case the airflow matches the one of the original fan at max speed. But needless to say this kind of fiddling voids the warranty and is always done a one’s own risk. The reward is a supply that is now hardly audible and much more pleasant to use.

20160409_FanController_021

The zip file contains the eagle files, PDFs and software of both revisions.

Arduino MPPT Solar Charger Shield – Software

There have been two previous posts on this project: one on the concept and the hardware and one on hardware testing. You probably want to check them out first if you’re not yet familiar with this project. Or even better: Click here for an overview over this project.

20160212_Projekte_045
Maintaining an input voltage of 17 volts even if that means a lower-than-desirable voltage at the output

Now that we know that we have a functioning MPPT solar charger we are ready to talk about the software (or the sketch as the Arduino folks call it). It’s quite simple, really. So this will be a short post. And yes, you can download the sketch. There is a link at the end of this post. As always, I appreciate any feedback, comments and the like.

There is a number of basic tasks the arduino needs to perform in order for this shield to be useful. I’ll go through them one by one.

Controlling the DC-DC converter

At the heart of this project there is a synchronous step-down (or buck) DC-DC converter that is controlled by a PWM signal from the arduino. So one of the tasks is to set the frequency and duty cycle of that PWM signal.

We let the PWM signal run at the maximum frequency the arduino allows with an 8 bit resulution. Thats simply 16MHz (the Arduino’s frequency) divided by 256 (the 8 bit resolution), or 62.5 kHz. So the prescaler will be 1.

As you can see from the shields’s schematic, we need to output the PWM signal from Pin 6 (by the Arduino’s pin numbering, not Atmel’s). In order to do this kind of low-level stuff you’ll have to read the Atmega328’s data sheet. There is usually no Arduino-ish shortcut if you really need to controll what’s going on.

Luckily it’s just a few lines of code to set things up. All in the function buck_setup(). There are three more little functions to control the DC-DC controller once it’s set up:

buck_enable() and buck_disable() are very simple and just turn it on and off, respectively. buck_duty(uint8_t duty) is only slightly more involved. It changes the duty cycle to the value you pass to it. Besides that it ensures that the duty cycle stays within certain limits.

20160212_Projekte_042
Test setup with resistor-based dummy load

You don’t want it to go to 100% since in order to keep the bootstrap capacitor C6 charged you need a little bit of off-time. In order to drive the upper FET you need a voltage higher than the panel’s voltage and that’s exactly what C6 is for. So we enforce an upper limit on the duty cycle.

Likewise, you don’t want your duty cycle to go below 50% because in that case you would be pumping energy from the battery to the pannel. A synchronous step-down converter is basically the same thing as a synchronous step-up (aka boost) converter with input and output confused. So we also want to enforce a lower limit on the duty cycle.

The upper and lower limits are set through the #defines DUTY_CYCLE_MINIMUM and DUTY_CYCLE_MAXIMUM.

Measuring voltage and current

The shield has all the hardware necessary to measure both voltage and current both at the input as well as on the output. We’ll just need to write some simple software to make good use of that hardware.

Unlike with the PWM singal where we had to do some low-level bit fiddling ourselfs we can just rely on convenient Arduino library functions to do the job. Basically, analogRead() is all we need here.

20160212_Projekte_043
Nicely regulating so that the input stays at 17 volts

I’ve written a function called read_values() that uses analogRead() to read all 4 values (input voltage, output voltage, input current and output current) 16 times each, averages the results and converts the ADC reading to proper voltages and currents.

The necessary multipliers are defined as floats in VIN_MULTIPLIER, VOUT_MULTIPLIER, IIN_MULTIPLIER and IOUT_MULTIPLIER. I’m doing all the voltage and current measurements in floating math. Yes, this is not at all efficient but we don’t need the Arduino’s computational power for anything else most of the time so this is fine here. Just keep in mind that you can save a lot of resources here if you ever need to do so.

Displaying voltage and current on the LCD

Our hardware also involves a 2 lines x 16 characters LCD so we can show the world what we are measuring. Again, we can rely on standard Arduino functionality to do the job. There is an LCD library that does everything we need.

So my function write_display() can focus entirely on formatting. The upper line shows the voltages in Volts, the lower line shows the currents in Milliamps. The input is on the left hand side of the display, the output on the right.

Deciding what to do

In the first section we’ve discussed the functions necessary to controll the DC-DC converter. But in order to use those functions, the Arduino needs to first decide what to do.

Vin21
66% duty cycle at 21V input voltage gives the desired 13.8V at the output

This is where the function buck_update() comes into play. You could consider this the heart of this sketch. This is where all the relevant decisions are made. When to turn the converter on, when to turn it off, when to increase the duty cycle, when to decrease it… You get the idea.

The behaviour of buck_update() is controlled by 8 #defines. I list them here together with the values I have used:

#define ENABLE_VOLTAGE 18.0
#define DISABLE_VOLTAGE 15.0
#define INPUT_TARGET_VOLTAGE 17.0
#define OUTPUT_TARGET_VOLTAGE_LOW 13.8
#define OUTPUT_TARGET_VOLTAGE_HIGH 13.9
#define INPUT_CURRENT_LIMIT 2000.0
#define OUTPUT_CURRENT_LIMIT 3000.0
#define INPUT_CURRENT_MINIMUM 0.0

I think they are quite self-explanatory, especially if you look at how they are used inside buck_update. It’s quite simple: If the panel’s voltage rises above 18V, turn the converter on. Once the converter is on, try to archieve a panel voltage of 17V without exceeding 13.9V at the output. If the panel’s voltage drops below 15V turn the converter off again.

Vin17
At 55% duty cycle with a 16.9V input voltage we’re getting only around 9.2V at the output

Besides that the function is also looking at the input and output current and makes sure certain limits are not exceeded. But with a 30W panel it should never be possible to reach those limits anyway.

Putting it all together

Now all we need to do in the loop() function is calling read_values(), buck_update() and write_display(). Since writing to the LCD is quite slow we are only doing it every 32nd time we read the values and update the PWM signal.

With this sketch I’ve hooked the MPPT Solar Charger up to my lab power supply. (a Keysight E3645A, my newest toy *g*) and my extremely simple but occasionally useful resistor-based dummy load.

The enable and disable voltages are simple and work as expected. Maximum output volage is also not tricky. If the voltage at the output goes too high, the duty cycle is decreased and everything is fine again.

20160212_Projekte_041
There’s not much to photograph when you’re writing and testing software

More interesting was to see how the shield would regulate when faced with a limited current budget at the input. For that the supply was set to a voltage of 21V (about a 12V solar panel’s open-circuit voltage) with a current limit of 100mA to 500mA. That’s quite a nasty supply, quite a bit trickier to handle than a real solar panel. Try to pull just a bit too much current and the voltage will drop to zero…

Also, the resistors at the output are not a realistic load for the converter. A car battery will pull no current at 12 volts or so (unless overly discharged) but will quickly start to sink large currents when the voltage goes just a bit higher and the battery is charging.

But I think the setup is good enough to test the sketch. And it handles the challenge quite well. With all resistors on (i.e a 100/6 ohms load) and a 300mA current limit, the input voltage sits at 17V (our target input voltage) while 9.25V appear at the output. At 400mA, the output voltage rises to 10.7V with the input still at 17V. At 600mA the input is still at 17V but with the output now at 13.15V. If I take the current limit even higher, the output voltage rises to 13.82V but not any higher, just as we want. The input voltage rises to 21V (since this is a lab supply and not a panel) with a corresponding drop in current to 530mA.

20160212_Projekte_044
Quite realistic: The charger is pulling as much current as it can with the current limit at 530mA and reaches an output voltage just above 12 volts

I’m honestly quite happy with the project as it is now. The idea definitely works and I’m motivated to design a new, deployable version with some fancy features that will use much less power at the same time. I’ve already done quite some work on that new version but it will take another few weeks until I get to describe that project here.

Until then I will show you some other, smaller projects that I’ve already finished but didn’t have time to document yet. So you will first see a number of smaller, simpler projects over the next few weeks.

Before I forget: There’s the Arduino sketch for download. And click here for an overview over this project.

Update: Now there’s an entirely new design.

Stand-alone Incuctance Meter Finished

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.

_MG_1183

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.

_MG_1190

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.

_MG_1193

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.

_MG_1188

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.

_MG_1202

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 _MG_1192

Arduino Ultrasonic Anemometer Part 12: Working on an Arduino library

This is just a very brief update on what I’ve been working on the last few days. By now, this blog has caught up with where the project currently stands so the blog posts won’t be quite as frequent as they used to be. When I just started this series I had already worked on this my wind meter project for two months so I had plenty of material I only had to post.

_MG_1080
Arduino Ultrasonic Anemometer Shield waiting for software

By the way: If you’re new to my Arduino-based ultrasonic wind meter project, you might want to click here for an overview: http://soldernerd.com/arduino-ultrasonic-anemometer/

As you can see in my last post, all the hardware is working really beautifully now so I can focus entirely on the software. So far, the software was really basic, just enough to show the hardware is working. That’s changing now. I’m working on a library to handle all the low level stuff, like setting up Timer1 and handling the interrupts.

One advantage of putting all that stuff in a library is that I can write in native assembler (as opposed to inline assember which I find a pain in the arse). Not everything will be written in assember. But the two I interrupt service routines (ISRs) will be. Everything else will be regular C code I guess. told you in an earlier post that my ISRs were surprisingly slow: around 5us for the most trivial tasks. The TIMER1_COMPB ISR is now re-written in assember and performs about four times faster. For simple tasks, the interrupts take only around 1.2us now.

It took a while but it’s finally ready. Click here for the next post: https://soldernerd.com/2015/01/01/arduino-ultrasonic-anemometer-part-13-arduino-library-finally-ready/

Arduino Ultrasonic Anemometer Part 8: More Software

In my last post I talked about how to get the Arduino to output bursts of 40kHz pulses. Today I’ll go through the rest of the software so by the end of this post we’ll have a very rudimentary but working sketch for our ultrasonic wind meter.

Click here for an overview over this series of posts on the Arduino Ultrasonic Anemometer: https://soldernerd.com/arduino-ultrasonic-anemometer/

overview2_1ms
Overview over one round of measurements, i.e. each direction is measured once in turn.

If you’ve read part 7 of this series you will have noticed that all the key tasks are handled not in the main code but in interrupt service routines (ISRs). That’s fairly typical for an application like this one.

In this project, there are 2 ISRs:

  • TIMER1_COMPB Interrupt: It is triggered by Timer/Counter1. It sends 15 PWM pulses every 2ms and takes care of the Axis, Direction and Mute signals. Named TMR_INT on the screen shots in this post. This is what I’ve covered last time.
  • TIMER1_CAPT Interrupt: This is where all the measurement takes place. It is triggered by the envelope detector and zero-crossing detectcor. It reads the current value of Timer/Counter1. Named CAPT_INT on the screen shots in this post. This is what I’ve covered last time. This is mainly what I’ll be covering today.

The basic Idea of the software is as follows:

  1. Every measurement takes 2ms. It takes 375us (15 times 25us) to send the pulses plus 500us – 1500us for the pulses to arrive (assuming very extreme wind situations). So 2ms gives us plenty of time to finish our measurement.
  2. Shortly after sending the pulses we start listening and wait for the envelope detector to trigger TIMER1_CAPT interrupt. We save the current value of timer1, this is our coarse measurement of time-of-flight. We then set up interrupts to capture a rising edge of our zero-crossing detector (ZCD).
  3. A rising edge of our ZCD triggers TIMER_CAPT interrupt. We save the current value of timer1 and set up interrupts to capture a falling edge of the ZCD.
  4. A falling edge of our ZCD triggers TIMER_CAPT interrupt. We save the current value of timer1 and set up interrupts to capture a rising edge of the ZCD.
  5. Repeat steps 3 and 4 until we’ve captured 8 rising and 8 falling edges. Averaging these will give us a very precise measurement of the phase shift.
  6. After every measurement we change the direction we measure: N->S, E->W, S->N, W->E, …
  7. We measure each direction 32 times until we calculate the actual wind speed. So one full measurement will take 4 x 32 x 2ms = 256ms. So we take about 4 measurements per second.
overview2_172us
Overview over a single measuement

The screen shot above shows how a measuement proceeds: AXIS and DIRECTION are set depending on the direction to be measured. MUTE is driven high and 15 PWM pulses are sent. TMR_INT triggers after every pulse in order to count them. After a short break, TMR_INT triggers again and turns MUTE off again. Eventually, the envelope detector (ENV_DETCT) triggers CAPT_INT. Shortly afterwards, CAPT_INT is triggered 16 more times by the zero-crossing detector (ZCD).

overview2_35us
Close-up of the actual measurement.

There are 2 sets of variables to save all the measurements from the envelope and zero-crossing detector: At any point in time, one is in use by the ongoing measurements, i.e. they’re being updated. The other set represents the last set of measurements and is static. This second set can be used by software in our main loop to calculate the wind speed and direction. As I’ve said, capturing one set of measurements takes 256ms. So we also have 256ms to do all the calculations, send data (via USB or whatever), write the new measurement to the display, do some data logging or whatever else we have in mind. There is likely to be some floating-point math, square roots and tigonometric functions going to be needed to arrive at the wind speed and direction but 256ms should be pretty comfortable even for that.

overview2_8ms
A long series of measuements. Look at the cursors: It takes about 25ms to do our calculations.

This is what I’ve tried to show in the screenshot above: There is a signal named CALC which is driven high when a new set of measuements becomes available and driven low when the calculations are finished. So this signal shows you how much time the Arduino’s Atmega328 spends processing the data and writing to the display. As you can see, it’s less than 25ms so there is ample of room for more complex calculations or other tasks. We’ll definitely need some of that head room since the calculations performed so far are really just the bare minimum.

There definitely is still a lot to be improved, mainly how the raw measurements are evaluated to get the actual wind speed. But what’s more important to me at this time is that the basic idea/setup works. With no wind, my measuements fluctuate somewhere between plus/minus 0.3 meters per second without having done any calibration. It also reacts nicely when I blow a bit of air towards it.

I’ve changed the pinout many times while developing this software but I’m confident that I won’t have to change the pinout any more. So my plan is to now build version 2 of the hardware first. The entire setup will be much less complex (and prone to errors) without all the lose wires going back and forth between the different boards. Then, with the updated and hopefully final (or nearly final) hardware I’ll go ahead and finish the software.

Speaking of software: You can download the Arduino sketch from the overview page where you also find the Eagle files for both boards: http://soldernerd.com/arduino-ultrasonic-anemometer/. I’ll make it a habit to post all the download material for this project on the overview page so people don’t need to go through all the posts trying to find a certain file.

That’s it for today, continue here to my next post of this series: https://soldernerd.com/2014/11/25/arduino-ultrasonic-anemometer-part-9-a-new-hardware/

Arduino Ultrasonic Anemometer Part 7: Basic software

Today I’ll tell you how I got started with my software. If you’re new to my blog you might want to click here for an overview over my arduino-based wind meter project: https://soldernerd.com/arduino-ultrasonic-anemometer/

The first thing we’ll need to archive is to send a series of pulses at 40kHz which is the frequency the ultrasonic transducers work. They must be as precise and repeatable as possible since all our measurements depend on them. Any jitter and the like will affect our measurements. And the duty cycle should be 50%. So you really want to do them in hardware. The Atmega328 comes with a single 16-bit counter/timer (Timer/Counter1) as well as two 8-bit counters (Timer/Counter 0 and 2). We’ll need the 16-bit resolution so the choice is clear: Timer1.

Timer1_output
Sending pulses using timer/counter1

Well yes, you could easily use one of the 8-bit counters to generate your pulses but you’ll still need timer1 for measurement. I’ve decided to do everything with just one timer so it’s going to be timer1.

How many pulses we should send is not so clear. I’m working with 15 pulses which works quite well but I’m not claiming it’s an optimal choice. But it is short enough to make sure we’ve stopped transmitting before the first sound waves reach the opposite transducer, even with heavy tail wind.

Since we have such strict requirements for our pulses, we can’t rely on any of those convenient high-level functions to set up our timer but have to study the Atmega328 datasheet and do it ourselfs.

This is what I have done:

pinMode(10, OUTPUT);
TCCR1A = 0b00100011;
TCCR1B = 0b11011001;
OCR1AH = 0x01;
OCR1AL = 0x8F;
OCR1BH = 0x00;
OCR1BL = 0xC7;

This is a short explanation of what it does: Set pin 10 as an output. Arduino pin10 is pin16 of the Atmega328. And that’s the pin connected to the output B of timer1. That’s line 1.

I then set up counter1 in FastPWM mode running at the full system clock frequency of 16MHz. Output B (that’s our pin 10 on the arduino) is set high when the counter starts at zero. It will be cleared (i.e. set low) when the timer reaches the value in output compare register B (OCR1B). The counter will be reset when (i.e.it will start at zero again) when it reaches the value in couput compare register A (OCR1A). I also enable an interrupt for when the timer overflows. More on that later. That’s lines 2 and 3.

Then comes the part where I actually set duty cycle and pulse with. I do that by setting the output compare registers. OCR1AH and OCR1AB are the high and low bytes of register OCR1A. So the final value in that register is 0x018F which equals to 399. That means counter 1 will count from 0 up to 399 before it starts again. That’s 400 steps. And here’s the math: The timer runs at 16MHz, our counter will overflow every 400 cycles. 16000000 / 400 = 40000. That’s exactly the 40kHz we’re looking for. The duty cycle is set to half that time by setting OCR1B to 199 or 0x00C7.

That’s it. We have a perfect PWM signal at exactly 40kHz and 50% duty cycle. Look at the screenshot above to convince you that this is exactly what we are getting.

But so far, the pulses go on forever. What we need is a way to turn the output signal off after 15 (say) pulses. One way of doing that is to count the pulses and turn the output off once the 15 pulses have been sent. That’s what the interrupt at overflow is used for.

In that ISR (interrupt service routine) I increment the variable pulse_count. Once pulse_count reaches 15 I know that all the pulses have been sent and turn the output off: TCCR1A = 0b00000011; The timer/counter will continue to run but the PWM output has been turned off.

For debugging/monitoring purposes, I set pin A5 high at the beginning of the ISR and low at the end. So I can tell when (and how long) the ISR is running by monitoring pin A5. Here’s what I get:

Timer1_overflows
Pulses sent (yellow) and time spent in timer interrupts (blue)

The yellow signal is the PWM output (pin10) as before. The blue line shows the time spent handling the interrupt. I could then continue counting without sending any pulses and turn the output back on when I reach 80 for example. And at the very beginning that’s exactly what I did. But then the microcontroller has to handle an interrupt every 25us (microseconds) even when not sending pulses. That’s quite wasteful so I set a longer time period by increasing the OCR1A and OCR1B registers seen above.

Actually, I’m using this interrupt to do some other things as well such as setting Axis and Direction as well as the Mute signal and some other housekeeping. That wide blue pulse you see at the left side of the screenshot above does most of that, that’s why it is so wide.

TimerInterrupt
Time consumed handling a regular timer overflow interrupt

Speaking of time consumed handling interrupts. It’s quite significant as you can see here: About 5 microseconds for a normal (just counting) interrupt. That’s 20% of CPU time while sending pulses (5us every 25us). That’s muuuch more than I ever imagined it to be. That’s about 80 instructions. I’m writing in C so I’ll have to check the assember code produced by the compiler to see what’s going on.

Click here for the next post of this series: https://soldernerd.com/2014/11/23/arduino-ultrasonic-anemometer-part-8-more-software/