Tag Archives: software

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.

Stand-alone Incuctance Meter Finished

If you’ve read my last post you’re already familiar with my Inductance Meter project: http://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 (http://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: http://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: http://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: http://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: http://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: http://soldernerd.com/2014/11/23/arduino-ultrasonic-anemometer-part-8-more-software/