Introduction: How to Design and Build a MPPT Solar Charger Using Arduino
Introduction
I had a busy retirement life before COVID19 lockdown. To battle the lockdown boredom, I built an off grid solar energy system with a few 100W solar panels, a PWM charge controller, and 2 AGM lead acid batteries of 100AH for energy storage.
The AGM batteries performance was disappointing. Based on my own measurement, its capacity was 20% less than the advertised rating. To maintain long cycle life, the battery shouldn't drain more than 50% of its capacity. Effectively my AGM batteries were 40AH. Also lead acid battery charging efficiency when capacity was above 90% full was very poor. Somewhere below 50%.
Charging efficiency was defined as the ratio of energy stored in the battery over input charging energy.
40AH wasn’t good enough for me, so I switched the lead acid batteries to 18650 Lithium batteries using battery cells salvaged from the old laptop batteries. Using brand new 18650 cells were fine. But it was expensive. I purchased a few of the cheap 18650 cells from Amazon, eBay, Aliexpress. All of them were fake or they were used cells. My other alternative was to buy it from a used store, like Battery Hookup.
Lithium batteries were cheaper, light weight, and more capacity. But the lithium battery was dangerous. A bad cell could self ignite. If overcharged, it could explode as shown in the picture which I over-charged it accidentally. For a safer alternative, use a LifePo4 battery.
To ensure lithium batteries safety, a battery management system, BMS, was employed. Its main function was to stop the battery from overcharging, over discharging, and short circuit.
Lithium batteries were usually connected in series of 3 or 4 battery banks for a 12V system. A battery bank was a bunch of battery cells which were connected in parallel. Every battery bank was packaged such that they had identical storage capacity. A good capacity difference between banks was less than 1%.
With serial connected banks of battery, current flowing through them would be the same either charging or discharging. As lithium batteries went through cycles of charging and discharging, they would lose their storage capacity.
The rate of deterioration was not constant. A lesser capacity battery bank would deteriorate faster. Even with mismatch between batteries starting with less than 1%, the discrepancy in capacity would grow as the batteries cycle through charging and discharging. At certain points, the battery bank with lesser capacity would have noticeably less capacity and would charge up faster than other banks.
A fully charged 18650 Lithium battery, its voltage was around 4.2V. With 3 banks of batteries in series, their combined voltage was 12.6V. During charging, the less capacity battery bank would reach 4.2V first, while the other 2 banks would be under 4.2V. Their combined voltage was less than 12.6V.
If continued charging to 12.6V, the least capacity battery could be overcharged beyond 4.,4V, and caused explosion.
To avoid over charging, a BMS was employed. With my understanding, BMS operated in this way. The BMS monitored voltage across each battery bank. If any one of the banks was over 4.2V, It shorted that bank plus and minus terminals with a resistor to bleed current out and lower its voltage. This allowed for other banks of batteries to charge up and maintained its voltage not above 4.2V. Once all the batteries charged up to 4.2V, BMS shut off the charging current. This process was called balancing.
To me, balancing provided no real benefit. It only improved slightly on power storage. It did not improve the overall Amp-Hour capacity because the overall Amp-Hour was limited by the battery bank with the least capacity.
If trying to consume energy from the battery once the least capacity bank already drained down to 3.3V, that would be over discharged and damaged to the least capacity battery bank. In the other situation when the least capacity bank was fully charged. BMS started to drain energy out of the least capacity higher voltage battery bank for balancing. That put extra stress on that battery bank and speeded up its deterioration.
Solar panel generated a limited amount of energy. It was a waste on stored energy for the sake of balancing. It was better off to stop charging if any battery bank reached 4.2V.
I couldn't find a MPPT charger that had a built-in BMS. They were sold separately. I decided to make my own since I had a lot of free time caused by COVID19. My MPPT solar charger had a build in BMS and did not have the shortcoming I addressed.
During building up my prototype, I burned a ton of electronic devices due to my mistakes. Maybe some of them were caused by parts I purchased from Aliexpress. Anyway still my fault in ordering parts from those fake dealers for penny pinching.
Learning from those failures, I finally made a working prototype. So far it worked faultlessly for weeks. I kept my finger crossed.
My design was limited by my first choice of salvaging parts from my old electronic junk pile. My pile was small. Out of necessity, I took risk ordering parts from eBay or Aliexpress. Ordering from Amazon was not that great either. But the refund was fast.
There were a lot of ways to improve my circuit. I already had some ideas on what to improve. It will be implemented in my next version.
Also my circuit should have hidden bugs. Hidden bugs was a necessary evil which provided job security for designers. It was ingrained into designer DNA for survival. Subconsciously the designers were blinded from seeing their own created bugs. I was no exception. So I needed my readers' help to point out the bugs for me.
My goal is not to show how to build the prototype step by step. My goal is to show how my circuit works so that others can use my idea to build a better one.
It is not financially worthwhile to build this prototype to save money. My energy cost saving is less than $0.2/day. It will take 20 years plus to recuperate my investment. But the fun of building it is money well spent.
The circuit example in this article is for proof of concept and for educational purpose only. Use at your own risk. Design a new one for yourself, and take your own risk.
Copying and using my design as you please. Damn it, don’t patent my idea. I am for open source.
You are welcome to point out my mistakes, give me constructive suggestions, or ask me for clarification. If you need help with circuit issues related to this article, feel free to contact me. If I have a solution, I will respond in my free time. Most likely I don't because I only have a few good brain cells left and the rest of my good brain cells were consumed during 40 years sweating out in the electronic industries which were run by Wall Street.
Maybe we can work together to solve the problem. Based on my track record, be prepared to have a lot of good parts ready to burn up.
Step 1: Material Requirement
1. Analog Circuit Simulator,
List of freewares
LTspice My primary circuit simulator. LTspice download link
MicroCap Discontinued , but still available for download Micro-Cap Download
Multisim Free if you are university student Multisim download
EasyEDA Good for making PCB EasyEDA download
Followings are not necessary if you just want to design but not to build.
2. Oscilloscope
Real time bandwidth >= 100 MHz
Channels >= 2
Buy the best oscilloscope you can afford. It is worth the money in the long run.
I had the cheap Hantek DSO5102P due to budget constraints.
3. Digital Multimeter
A simple 4 digits DVM is fine. Better with capacitance and inductance meters.
4. Variable power supply
Output voltage range: 0v to 20V,
Output current > 2A
5. Signal Generator.
Sine wave, square wave with adjustable pulse width
Frequency output > 1MHz
6. 100 Watts Solar Panel
At least 1 panel
7. Lithium Batteries
Minimum 3 batteries Total Capacity > 10 Amp Hours
8. Soldering Iron, soldering wire and flux
9. Arduino Nano. 328P
10. Max4080
Current sensor made by Maxim
11. NRF24L01
2.4GHz RF transceiver Optional for remote wireless communication
12. ADS1115.
16 bits ADC. Be careful, 80% of my purchases were fake. They were a 12 bits ADC (ADS1015) in disguise. Once you receive it, test it out and get a refund if it is fake.
13. 12V to 5 V buck converter or any linear regulator like 7805
Output current > 0.2A. Any car USB plug-in will do. This is for powering up the prototype.
14. 20 by 4 LCD Display (Optional)
if you like to have a permanent measurements display.
15. PMOS IRF4905 or equivalent
I brought 20 of them for their low ON resistance from eBay. I tested the ON resistance, They were 80m Ohm. 22m Ohm listed in the data sheet. They were fake disguise by IRF9540.
I paralleled 3 of them in my prototype to improve heat dispiciation.
16. NMOS IRFZ44N or IRFL44N or equivalent
17. Schottky Diode.
2A continuous and 40A surge, Break down > 50V. I used SS3P5 by VISHAY.
18. NPN 2N2222A and 2N3094 or equivalent
19. PNP 2N3096 or equivalent.
20. Ferrite Toroid Cores
Cheap Toroid usually sold without specification such as relative magnetic permeability and saturation current. I brought the cheap Toroid 22mm x 14mm x 8mm from Amazon and characterized it myself.
21. Double sided PCB Prototype Board.
22. Capacitors 3300uF 50V, 4700uF 16V, 4.7uF 50V, 0.01uF, 0.1uF
23. 10 Watts 0.02 Ohm Resistors
I parallel five 2 Watts 0.1 Ohms resistors to make a 0.02 Ohms for current sensing up to 20A
24. 1/4 W 5% Resistors 100K 51K, 10K, 4.7K, 2K, 1K, 100
25. 1/4W 1% Resistors 3.3K, 2K, 3.9K, 3.3K, 22K 7.5K 1K.
1% resistors are used for voltage dividing for accurate voltage sensing.
26. Connecting wires.
I used AWG 30, AWG 24, AWG 18 silicon wires for their flexibility and their insulation won't melt during soldering.
27. AWG 12 solar panel connection wires with connectors.
28. Connection terminal block strip.
29. Smoke alarm. Just in case the Lithium battery accidentally caught fire.
30. A solid metal box
Large enough to hold all the batteries and the prototype inside to prevent fire from spreading. I used my old PC metal casing. A 100 Amp-Hour is a lot of energy. So far nothing is hotter than 40C in my prototype and no battery hotter than 20C. The construction of 18650 is safer than the lithium polymer cell inside our cell phone, tablet and power bank. Their chance for them to be self ignited is slim but not 0.
Step 2: Preparation
Before proceeding to build the circuit, Install an analog circuit simulator. It is a must have for analog circuit designers.
Install any analog circuit simulator you like. Get yourself familiar with it. Lot of simulator tutorial videos available in YouTube Create your own simple circuit. If you don’t have one, download my schematics Charger0.asc and my model library SUB_LIB.txt to your simulation working directory and play with it. If you are not using LTspice, you need to manually enter the circuit netlist and the model file to your own simulator.
Attachments
Step 3: Solar Charger Background Information
PWM Solar Charger
My understanding on PWM was based on disassembling a cheap PWM Solar Charger made in China. I bought it from Amazon. It might not be representative of most PWM chargers. This PWM Solar charger was a simple pulsing ON/OFF switch that connected between the solar panel and the battery. It transferred energy from a high voltage level solar panel to a low level voltage at the battery.
The voltage step down was achieved by the fast ON/OFF switching of M1 that generates an AC voltage to C1 which acts as a low pass filter.
Rsource is the internal solar panel impedance. Rwire is the connecting wire impedance. Rbat is the internal battery impedance. M1 is an ideal MOS, with ON resistance = 0
This type of energy transfer has inherently poor efficiency.
When M1 is ON, Current sourcing out from panel = Current input to the battery =I
Power output by Solar panel = Vsolar * I
Power input to battery = Vbattery * I
Efficiency = Power input to battery/Power output from Solar = Vbattery/ Vsolar
This equation shows the efficiency is worse at high voltage difference.
Power loss = Power from Solar - Power input to battery = (Vsolar - Vbattery) * I
All the power lost is dissipated in Rsource, Rbat, Rwire as heat. Lowering those résistance will not improve the efficiency.
If resistance = 0, the current I will be infinity.
This PWM charger rated 50A. Based on what was inside, there was no way it could carry 50A.
On a sunny day afternoon, my 100W solar panel through this PWM bulged up my 7AH lead acid battery. I measured the battery voltage, it was above 16V.
If your PWM charger looks like mine, check the model number. If it is the same, you know what to do.
Step 4: Maximum Power Point Tracking, MPPT.
MPPT extracts a maximum amount of energy from solar panels and converts it to a lower voltage to charge batteries. MPPT employed a buck converter scheme to do the power transfer for its high efficiency.
How Buck Converter works
This is my 2 cents explanation on Buck Converter based on idealistic electronic components.
An ideal PMOS, ON resistance = 0, switching delay time = 0.
An ideal Schottky Diode, forward resistance = 0, backward resistance is infinity, and forward ON voltage = 0 and no reverse breakdown.
The buck converter looks similar to a PWM charger, except it has an inductor in between the PMOS and the battery..
Current flows through the resistors are wasted energy of I^2 R which converted to heat. To make all those resistors negligible, an inductor is added. With PMOS switching ON and OFF at a frequency f, the inductor L1, will have an impedance of j 2𝝅f L1 which is much higher than the combined impedance of Rsource, Rbat and Rwire. So they are all removed from the schematic for simplicity.
When the PMOS is on, voltage across the inductor VL = Vi -Vo
Peak Inductor current IL=VL Ton / L1
The inductor current generates a magnetic flux inside the ferrite toroid.
The magnetic flux energy EL=1/2 * L1 * IL^2
When the PMOS is switched off, the inductor current flows will not stop instantly. The current will ramp down and continue pushing out until all the magnetic flux energy is transferred to the battery.
Energy transferred to battery Ebattery = Vo IL Toff /2
Stored magnetic energy = Energy transferred to battery
EL=1/2 * L1 * IL^2 = Vo IL Toff / 2
Solving the above equation Toff= Ton(Vi-Vo)/Vo
3 phases of energy transfer.
The green trace is PMOS gate voltage The Blue trace is the inductor current.
1st phase: PMOS is ON.
Current flows from the input through the inductor to charge up the battery and magnetize the inductor.
2nd phase: PMOS is OFF.
Current from the input halted. Inductor current cannot not be stopped instantly. Current continues pushing out from the inductor via the Schottky diode to charge up the battery until all its stored magnetic energy = 0.
3rd phase: PMOS is still OFF.
No magnetic energy is in the inductor. Power transfer is completed. No current is flowing. Everything sits idle until the next ON pulse. In the real world, the Schottky diode has parasitic capacitance. Together with the inductor, it forms a LCR circuit. The voltages and currents at the LCR network will ring and slowly die down.
Be sure to allow time for the 3rd phase to exist due to safety reasons. Lot of my PMOS blew up due to no 3rd phase. Without the 3rd phase there will be residue magnetic flux which will accumulate and increase every cycle. Subsequently the magnetic flux will exceed the saturation current limit of the ferrite toroid, current will surge and blow up the PMOS. It is fine to operate without the 3rd phase if you can control the inductor not goes into saturation. But it is tricky to achieve.
The buck converter is a highly efficient circuit. Ideally no power transfer loss in phase 1. Major power loss happens in phase 2 due to Schottky diode forward voltage drop . But Phase 2 only occupies a portion of the whole period, so its impact is limited. If employed synchronous switching instead of a Schottky diode, the losses will be reduced further.
The drawback for the buck converter is its input and output ground reference are tied to the same potential. So it cannot be used for balancing due to the in-series batteries negative terminal being at different voltage levels.
Step 5: Fly Back Converter
Following is my another 2 cents explanation on how FlyBack converters work with the assumption that every device is ideal.
The Fly Back converter uses a transformer to transfer power between input and output. Their input ground reference can be separated from output ground reference . Also the output voltage can be higher than input voltage or negative.
The transformer is built with 2 inductors L2 and L3 that their inductor wires are winded together in a single ferrite toroid
An ideal transformer has a magnetic coupling efficiency K1 =1.
L2 is the primary side of the transformer and L3 is the secondary side of the transformer. The polarity of L2 and L3 are opposite.
The green trace is current i2 in L2. The blue trace is current i3 in L3.
At phase 1, M2 is ON, current i2 ramps up in L2, and generates magnetic flux inside the ferrite toroid. Peak L2 current I2 = Vi Ton / L2 Where Ton is the on time duration of phase 1
The magnetic flux energy EL=1/2 L2 I2^2
At phase 2, M2 is OFF, the stored magnetic flux energy EL is converted back to electrical energy and pushes a ramp down current i3 out from L3 through the Schottky diode to the battery.
To maintain identical magnetic flux at the instant of ON to OFF,
I3 N3 = I2 N2 where N2 N3 are the number of turns in L2 and L3.
Then peak current of I3 = I2 N2 / N3
Energy to the battery Eb = Vo I3 Toff / 2 assuming Schottky diode Vf = 0
where Toff is the time duration of phase 2
Conservation of energy EL = Eb Solving the above equations
Toff = (N3/N2) (Vi/Vo) Ton
Average Current to output Iavg = 1/2 Vi Ton^2 / (L2 Tperiod)
By controlling Ton we can control how much power deliver to the output
Note: Inductance L is proportional to N^2
A Fly Back converter is less efficient than a Buck Converter because all the output current needs to go through the Schottky diode which has a forward voltage drop that dissipates power. Also a real transformer coupling coefficient K, is always less than 1 due to magnetic flux leakage. The leak magnetic flux is a form of wasted energy. The leak magnetic flux creates high voltage spikes at the primary side that need special circuits to control it.
Users should make sure of the existence of Phase 3. If not, the inductor might saturate. That means set Ton+Toff< Period
Because of the transformer isolation effect, I use multiple secondary windings flyback converters to do the battery balancing.
Step 6: Achieving Maximum Power Transfer
Solar panel provides a limited amount of power. MPPT is used to extract the most power out of it.
This simple schematic is for demonstrating how much power is being transferred from the source to the load at different load current.
Vs and Rs represent a solar power source open circuit voltage and its output impedance. This is not a real solar panel model.
I1 represents the buck converter since it is sucking current in proportional to the switch ON time Ton.
The simulation plot shows power, voltage and current with current vary from 0 to 20A.
Power transfer is 0 at both ends and peaked somewhere in between.
The MPPT needs to set the Buck Converter Ton such that it is landed at the peak of the power curve.
To set the buck converter to operate at the peak, we need 2 measurement points, a previous power measurement and a present power measurement.
If the present power is higher than previous power, we increment the buck converter Ton. otherwise we decrement the Ton.
In this way, the buck converter Ton will eventually operate at the peak within +/- 1 increment step.
Since the battery voltage changes very slowly and can regard it as a constant voltage. That implies power is proportional to current. Tracking can be done by using the measurement of previous current and present current.
Step 7: My Charger Controller With BMS
The circuit diagram is a simplified Charge Controller Circuit.
Theory of Operation
My charge controller is a combination of a buck converter and a flyback converter.
The buck converter was chosen for its high conversion efficiency. The fly back converter was chosen for its floating ground reference for balancing.
Opposite of the traditional balancing scheme which takes current out of a high voltage battery, my balancing scheme is to put more current to a battery with the lowest voltage.
The simplified schematic is simulatable in LTspice. It is used for fast parameters optimization, and detecting bugs . For its simplicity, it gave me better insight of the circuit operation.
Once I was satisfied with its performance, I ported it to a final design with all the auxiliary circuitries added. Then I ran long simulations on the final design to check out its performance. If its performance was satisfactory, I started to build the prototype. The Arduino Nano programming was the last step. I used a signal generator to control the Ton to substitute the PWM signal generated by Arduino Nano.
My advice to everybody, Don't spend time building the prototype first. Spend more time on the simulator. That will save a lot of grief in the future.
To run LTspice simulation on my circuit, load both Charger0.asc the and SUB_LIB.txt into your LTspice working directory.
Charge0.asc is the schematic netlist file.
SUB_LIB.txt is a model file. It consists of component models that do not exist in LTspice model files. I created it by copying a similar device models file from somewhere I googled and modifying the device parameters to make it behave close to the device data sheet. My models are approximations. There is no guarantee that the models will match the real device.
Circuit descriptions.
Cb4,Cb8,Cb12 are 1 Farad Capacitor with an initial voltage of 4V. They represent 3 lithium batteries which connected in series. Capacitor model has polarity. Don’t flip it since those capacitors have initial voltage.
M1, D1, L1 form a buck converter to charge the battery chain.
L1, L4, L8. L12 are 4 separate windings in a single ferrite toroid to form a multi-outputs transformer with a coupling coefficient of 0.97. Combined with D4, D8, D12, they form a multi-outputs fly back converter.
M4, M8, M12 are switches in series with D4, D8, D12 respectively. They are used to cut off current when the respective battery does not need to get the extra charge.
M4 is an NMOS, it is OFF when gate voltage is at 0V. When gate voltage is at 12V, it is ON. M8 and M12 both of them are PMOS. When their gates are at 12V, they are OFF. When at 0V, they are ON.
If M4, M8, M12 are all OFF, the converter acts like a normal buck converter. Green trace is L1 current Purple trace is D1 current All other traces are L4,L8,L12 currents, They are 0. The plot is on the left.
At phase 1, M1 switches from OFF to ON. L1 current ramps up and charges the battery chain and magnetizes the inductor L1. At phase 2, M1 switches from ON to OFF, all the magnetic flux energy becomes current and goes through D1 to charge the battery chain. There is no current flowing in L4, L8 and L12.
At phase 3, M1 is OFF. No current flows in the L1, L4, L8, L12 and D1.
If any of M4, M8, M12 is ON, During phase 2, the charger acts like a flyback converter.
Graph on the right plots inductor currents with M4 ON and M8,M12 OFF. At phase 1, there is no difference from before. M1 switches from OFF to ON. L1 current ramps up, charges the battery chain and magnetizes the inductor L1.
when M1 turns from ON to OFF, the converter acts like the fly back converter. Most of the magnetic flux energy goes through L4 and D4 to the battery Vb4. A small amount of leak inductor energy goes through D1 to the battery chain.
The blue trace is L4 current. This is the additional current that only pumps into the Vb4 battery. This is my balancing scheme to pump additional current to the battery with the lowest voltage.
To balance Vb8 and Vb12, just do the same thing by enabling the respective MOS switch. The additional current to the battery is equal to the magnetic flux energy at the end of phase 1 minus the leakage flux energy. The leakage flux is not wasted. This energy still flows through the battery chain, For balancing, the microcontroller senses all battery voltages and enables the switch to the battery with the lowest voltage.
This implementation is not limited to 3 outputs. For more outputs, just add more secondary windings, diode and MOS switch. Also this configuration can transfer power from one battery bank to the other battery bank by adding a switch across the diode. But there is no point to do this because the total capacity is limited by the least capacity bank.
Attachments
Step 8: Design Target
Output Voltage Range: 9V to 14V
Maximum Output current: 10A
Maximum Input voltage: 20V. (40 V was the initial target, Due to PMOS Vgs limit of 20V)
Switching clock frequency: 10KHz to 50KHz. Normally set at 20KHz.
Output short circuit protection.
Individual battery voltage sensing.
Sensing Input current, charging current, output current
Battery voltage balancing.
Battery over voltage protection.
Battery under voltage protection.
Input cutoff switch.
Output cutoff switch.
Temperature sensing.
Real time fault detection and shut down.
Wireless data communication at 2.4GHz.
LCD measurements display.
Step 9: Complete Charger
Solar Charger Ltspice netlist Charger.asc and model library SUB_LIB.txt
The complete charger has a lot of new circuit added. Their functions are explained in next step.
Attachments
Step 10: Input Control
It is an input switch and input current sensing. Both of them are optional.
The switch is for stopping current flows back to the input if the input voltage is lower than the battery bank voltage. If the input is connected to Solar Panel only, there is no need to have a switch since Solar Panel has a Schottky diode at its output to block current flows back.
The current sensing is for monitoring input current. Other than that there is no practical purpose. It can be removed.
Mi1 is the switch. Ri1, Ri6 Qi4 forms a voltage level converter, which converts Arduino digital output to turn on or off Mi1.
Rsin1 is the current sensing resistor. It is a 0.02 Ohm resistor formed by paralleling five 0.1 Ohms 2 Watts resistors. There are a lot of voltage ripple at the input nodes caused by switching of the buck converter. Cf1, Lf1,Cf2, Lf2 form a 2nd order low pass filter to filter the ripple voltages on the current sensing resistor that feeds to a current sensing amplifier MAX4080 then to a 16 bits ADC ADS1115A. The ADS1115A fastest conversion rate is 840sample/sec. So the corner frequency for the low pass filter is set below 840 Hz.
The IRF4905 I purchased does not have the low ON resistance as in the specification, so I put 3 of them in parallel.
Step 11: MOS GATE DRIVER
This is to translate the pulse width modulation pulses from Arduino to switch M1 on or off.
Ri7, Ri3, Qi3 forms an inverted amplifier and buffered by Qi1 and Qi2 emitter follower for high current gate drive.
My initial design target is 45V max input voltage. But I found out later there was a specification for IRF4905 max Vgs is +/-20. I don’t know why a rated 55V PMOS had a 20V limit on Vgs. If anybody knows please tell me.
For now, the input is limited to 20V. One time I tested my prototype up to 35V for a couple of minutes, it still works. But I don’t want to bring the voltage higher because I was afraid the whole prototype would blow up. The prototype took me weeks to build.
My solution is to put a ON/OFF current source to Ri3. That will limit the voltage swing. It will be implemented in the next version.
For the same reason of ON resistance, M1 is 3 IRF4905 in parallel.
Step 12: Buck and Flyback Converter
There are minor changes from the simplified circuit. D1, D4, D8, D12 are different. Some of the diodes were burned up due to wrong wiring or accidentally short circuit during probing. I ran out of diodes, and replaced them with the present ones. Both types should work fine.
Rs1 is 0.02 Ohms which is made of five 0.1 Ohms 2 W resistors in parallel to sense current out of L1. Due to current switching, there is high voltage ripple at Rs1.
Two 2nd order LC filters (Cf3, Lf3,Cf5,Lf5) were added to do voltage averaging, and fed to E1 and E2.
E1 and E2 are models of current sensing amplifier MAX4080 which has a differential voltage gain of 20.
Ms4, Ms8, Ms12 are voltage level translators to switch M4, M8, M12 ON and OFF. There is no reason why Ms4, Ms8, Ms12 are MOS not BJT. Same reason for the value of C2,C3,C5,C6. It was just my mood at that time. More capacitance means less ripple.
Current flows through Rs1 is used by the microcontroller to adjust the ON pulse width to get the maximum power available.
Step 13: Output Control
This is an output switch and output current sensing circuit. The output switch is to protect the battery from over discharged, and stop excessive output current.
Output current is sensed by Rsout. It is a 0.02 Ohms resistor formed by five 0.1 Ohms 2W resistors in parallel. The current sensing voltages are filtered by two 2nd order LC low pass filters, then fed the filtered voltages to the current sense amplifier MAX4080 modelled by E1.
Rload is not part of the circuit. It represents an outside loading.
Qo1 is for short circuit protection. If more than 30A at output, it shut off Mo1.
Co2 is used to turn Mo1 back on after a short circuit event. When Mo1 is off, voltage across the base and emitter of Qo1 is high, and collector of Qo1 is high too and shuts Mo1 off. By toggle Mo3 OFF and ON, voltage at Co2 will discharge to 0. Once Mo3 On again, it will pull the gate of Mo1 down and switch Mo1 back on. The reason to put in this feature instead of using software control. The delay time to switch off the output by software is greater than 20mS. It is too long for a battery bank that can supply hundreds of Amperes. Using hardware control, it is 50 times faster.
Mo2 and Ro1 were added to solve an issue discovered once the prototype was in use. The issue was when plugging in my 12V to 110V inverter would trigger this protective mechanism. The reason was the input capacitance of the inverter was huge even with the inverter off. Plugging in would generate a large electrical spark. I guess the current spike was in excess of 30A. I added Mo2 and Ro1 so that it can slowly charge up the inverter first, then turn on Mo1. For the same reason of high ON resistance, Mo1 is formed by 3 MOS in parallel.
Step 14: Battery Model
Those devices are not part of the charger. It is a model to mimic Lithium Batteries that are used for simulation.
Because of large capacitance, the simulation time is set to 1 second and captured the last 1mS data. This allows everything to settle in a steady state in order to measure power. That took my computer 6 hours to complete. For other types of measurement, 2mS simulation time is good enough.
Beware, simulation capacitors have polarity. Be sure the polarity is right when using an initial voltage (IC=4), otherwise the simulation result will be way off.
Step 15: Simulation Results at 10A Output.
Input voltage = 16V.
Switching frequency = 20KHz.
ON pulse width = 40uS
Charger output = 10A at 12V.
A 1.2 Ohms is placed at the output to generate a loading current of 10A.
Results
Input power = 131.1 Watts.
Output power = 118.9 Watts.
Conversion efficiency = 118.9/131.1 = 90.7%.
That matched the measurement on my prototype of 91% efficiency.
Step 16: Power Dissipation on M1
M1 dissipates the most power. Be sure its heat sink is large enough to sink the heat so that M1 internal temperature does not go above 80C at maximum loading.
Power dissipated by M1 = 1.5W.
Also check other devices for power dissipation, make sure they are not over their device limits.
Step 17: Measuring and Building Inductor From Ferrite Toroid
Inductors required are hard to purchase over the counter. Usually it needs to be winded up by the user.
To make an inductor, first is to find out the relative magnetic permeability, saturation current and its physical dimensions.
Physical dimensions are easy to get, just measured it by a ruler or a caliper.
To find relative magnetic permeability, saturation current, using the BH curve is a good way, but a little bit complicated.
I used a simple method to measure those parameters approximately . Engineer is not a scientist who needs to be exact. Engineers do things approximately and make them work in a wide range of variation. My design can’t meet the 6 sigma, maybe above 1 sigma.
Use AWG 24 magnet wire wind as much as possible to the toroid, but leave a gap in between so that it can be cut an air gap later to improve saturation current.
Build the test circuit as shown in the schematic,
The light blue trace is voltage on rm. The yellow trace is voltage on rp. The purple trace is the V( rp) - V( rm). The amplitude scale is 200mV/div or 2 A/div.
V(rp) starts to shoot up at 140uS after the M1 is ON. That is where the inductor enters the saturation region. The current is around 1A but hard to tell from the scope picture because the signal is small and with a lot of noise.
Even though saturation current is a soft round corner and accuracy does not matter that much, I still like a better measurement.
To get a better measurement, I measured voltage across R1. At the instant M1 is off, the peak current in the inductor flows into R1. R1 is 5 times higher, so the voltage across R1 is 5 times bigger than R2.
The reason for not making R2 higher is the current flows through the RL is an exponential function. The error will be much larger with higher R2 if using a simple linear equation to calculate inductance. I am not capable of handling equations that are not 1st order linear.
M1 off after 120uS ON time.
The blue trace is the voltage at D1 cathode.
The ringing at center is caused by the probe ground lead. The yellow trace does not bend up, It is just under saturation.
Voltage across R1 is 0.55 Ohms and voltage is 0.65V. Ipeak = 0.65V / 0.55 Ohms = 1.18A
The toroid saturation current Isat = 1.18A * 69 turns = 80 A-turn
Measured voltage at V5 = 5.28V
L = V Ton / Ipeak = 5.28V*120uS/1.18A = 537uH
This is an approximation since R2 and On resistance of M1 are ignored.
Step 18: Better Inductance Measurement
To have an accurate inductance measurement. Use a LCR band pass filter to find the peak amplitude at the node Vo by varying the input sine wave frequency. Then use the equation fpeak = 1/(2* pi * sqrt(LC)) to calculate L.
Yellow trace is Vi. Blue trance is Vo. Vo amplitude peaks at frequency 5.01 KHz.
fpeak= 5.01KHz, C=0.47uF
L = 1/((2*pi* fpeak)^2 * C) L =2mH
There is 4x inductance difference between both measurements. The minor reason is the switching method is less accurate due to small voltage. The main reason is the ferrite permeability is non-linear. The LC method measured at 0 current, while the switching method at the point near saturation.
To design a good charger, start with the inductance equal to the geometric means of the two inductance. Then simulate the circuit functionality with +/- 50% inductance tolerance. A good charger will be able to handle the inductance tolerance because both switching frequency and the pulse width are adjustable.
Step 19: Find the Relative Magnetic Permeability and Number of Turns for the Inductor
To find the relative magnetic permeability and number of turns for the inductor, use the equations shown in the picture. The reason of showing the step in picture because I don't know how to enter equations in this editor.
For more accurate results, go to the website Inductor Calculator and enter all the parameters, and press calculate. Then readjust the relative magnetic permeability number until it matches the LCR measured inductance
Step 20: Improving Magnetic Saturation Current
The picture shows the toroid saturation current limit is exceeded.
To increase toroid saturation current, use a bigger toroid or cut an air gap at the toroid.
Making an air gap is a better choice since the toroid size is smaller and its permeability is more linear that reduces the inductance variation.
Clamped down the toroid and cut it with a diamond blade using a rotary tool..
The gap spacing is between 1mm to 2 mm. The actual spacing doesn't matter. A wider gap, you just wind more turns.
Warning: wear safety goggles and cut with steady hands.
Measuring results after the cut.
Peak frequency =13.3KHz
L = 1/((2* pi * fpeak)^2 * C)=305uH
Step 21: Switching Measurement on an Air Gap Inductor.
The yellow trace is V(rp).
The up ramp corner is the magnetic saturation corner which is at 10% duty cycle.
The blue trace is V(rm),
The purple trance is V(rp) - V(rm) at 500mV/div.
The PWM clock is 500Hz, and the duty cycle is 15%.
The supply voltage is 5.0V
The zoom in plot is 10% duty cycle at the verge of saturation.
Purple trace: R2 current 5A/div
Blue trace: voltage at D1 cathode
The voltage step on the blue trace is 2.3V at the instant M1 is switched off.
Peak inductor current I = 2.3/0.55 = 4.18A
R1 =0.55 Ohms Frequency = 500Hz Duty cycle = 10%
then Ton = 200uS
Inductance L = VT/I = 5.0V *200uS/ 4.18A =240uH
The difference in inductance LCR measurement and the switching measurement is much smaller due to the permeability being more linear with an air gap.
With better linearity, the circuit only needs to design for +/- 20% variation in inductance.
The saturation current for the air gap inductor = 4.18A * 68 turns = 284 A-turns
The air gap toroid inductor is 305uH with 68 turns
Then k = L/N^2= 305x10-6/68^2 = 0.066x10E-6
L = 7uH,
N=7u/ 0.432x10E-6 = 10.3 turns
With 284 A-turns,
A 10.3 turns inductor, the maximum Ipeak = 284/10.3 = 27.6A,
If Ton / Tperiod=1, Ipeak = 2* Iavg , base on integrating the area of a waveform with a triangle shape.
For Iavg = 10A,
Then minimum Ton / Tperiod ratio = 27.6/20 = 1.37.
From another angle, there will be a 37% margin on output current.
With adjustable frequency and pulse width, it is possible to set a condition for the charger to have an output of 10A and peak inductor current does not exceed the saturation current limit.
Step 22: Battery Management System Schematic
Circuit Description:
Microcontroller
Arduino Nano uses Atmega 328p microcontroller. I chose it because it was inexpensive, small size, had a lot of free tutorials and a lot of open source supporting libraries.
Its functions are to process all measurements to control the charger, to display measurements and wireless data transfer. It runs at a 16MHz clock. It is not fast but fast enough for battery operation.
It has a build in 10 bits ADC, but not precision enough. I used it for temperature sensing and non-critical voltage measurements.
Current Sensing Amplifier MAX4080
It is a current mode positive voltage high precision differential current sensing amplifier.
It has a gain of 20. Its common mode rejection is above -100dB. I measured it with a 10mV across its inputs and raised the common mode voltage from 0V to 40V. Its output remained constant at 200mV.
Before I built a differential amplifier using an OPAMP and precision resistor divider. My differential amplifier had much worse performance. So I replaced them with MAX4080.
It is a current mode differential amplifier. If placing resistors in series with its inputs, it will greatly degrade its common mode rejection. That is the reason why I used LC filter instead of RC filter.
MAX4080 senses current in one direction only.
16 bits Analog to Digital Converter ADC, ADS1115
There two ADS1115. They are for measuring all the divided down sense voltages and sense currents.
It uses I2C to communicate with the microcontroller. They are hard wired into 2 different addresses.
The reason for using ADS1115 instead of the built in Arduino 10 bits ADC. The Arduino ADC is noisy even with a clean input signal. My measurements showed the Arduino ADC digital output standard deviation is 2 counts. If designing for 3 sigma, the effective ADC bits reduced to 7.5 bits. The internal reference voltage is poor. I tested 10 parts, their reference voltages varied from 1.06V to 1.08V. The spread is 20mV, That is 2% inaccuracy.
To measure voltage across a battery, we need to subtract two voltages. Measuring voltage at 8V and 12V, with a 10 bits DAC, at best, 1 bit resolution is 8mV and 12mV respectively. After subtraction, the effective resolution is 20mV. A 20mV resolution is not good enough when combining with other inaccuracy.
I chose ADS1115 because it is easy to use and inexpensive. Its measurement is accurate. It has many library supports, but I don’t use those libraries because they don't have parallel conversion triggering. I created my own codes to do parallel ADC conversion. .
I measured multiple parts at 4V input range. They had an accuracy less than +/-0.5mV, which is much better than I needed. One minor drawback was the input biasing current. If using 6V input range and Vcc=5V, with input higher than 5V, there is a significant amount of input bias current going into the chip. I don't use the 6V input range and only use the 4V input range.
Another drawback is its slow conversion rate. Its fastest rate is 840 samples/sec. I used 2 of them to double the speed. I triggered both ADC to start conversion. Then wait for 1.2mS then read both ADC values. All the existing libraries do not support this feature.
There are 0.01uF capacitors soldered near the ADC inputs to filter noise.
There are a lot of fake ADS1115 in disguise by ADS1015 which is a 12 bits ADC.
A 12 bits ADC has fine resolution that is small enough to fool most people. Test it out once you receive it. It is relatively simple to test it out. Set the chip input range to 4V. Put 0 to few mV at the input and read the ADC results.
For a 16 bits ADC, due to noise, its output can be any number 0,1,2,3,4,5,6,7,8,9,10,...... But a 12 bits ADC will read 0, 16, 32, 48, 64 …... . There is no value in between the multiple of 16.
Voltage divider.
Voltage divider is for dividing voltage down below 4V before feed to the ADC.
I used 1% resistors as voltage divider to reduce temperature drift at the divider output.
Their absolute values are not important, because in the Arduino codes, there are calibration coefficients to match the division ratio.
Voltage Regulator
The 5V voltage regulator takes power from the 12V battery and converts it down to 5V to power up the Arduino, ADC, NRF24L01, LCD display, The maximum power consumption is less than 200mA.
I used a buck converter with LC filter at the output to reduce noise coupling into the ADC. If you do not care about a few mV noise, doing without the LC filter is fine. If using a linear regulator, there is no need for the LC filter.
A manual switch is placed at the regulator input to power off the system.
R17 at Arduino D5 is for pulling down the enable output signal when the power is shut off.
LCD display
LCD is a 4 rows by 20 columns display. It displays voltages, currents, input and output Amp-Hours and status of the charger. It is using I2C to communicate with the microcontroller.
Wireless Communication NRF24L01
It is a 2.4GHz communication device. I chose it because it is simple to use, inexpensive, and has a lot of supporting libraries and tutorials. It can transfer data at 2Mb/s anywhere in my home. My data package is only 32 bytes. I use the slowest data speed of 250kb/s for the high signal integrity.
There is a library which most tutorial videos cited is not that great. I used it at first, but I had trouble with reliable data transmission for distance over 10 feet.
I came very close to reading the data sheet and writing my own code. Then I discovered the NRFLite.h library. Once I used NRFLite.h in my code, my data transfer was great.
Interrupt
There is a button X11, which is used for interrupt the microcontroller. The interrupt routine tells the microcontroller to write the measurement data into its internal EEPROM in normal operation.
It is for debugging. Once the interrupt routine is done, the microcontroller will return to what it was doing before.
If the microcontroller detects any fault, it will save all measurement data to EEPROM Then shut down the system.
At boot up, Arduino routine will display both failed measurements and the saved measurements before powering up the system. User has to press the interrupt button to continue. That gives time for reader to read the measurement data.
Temperature Sensors
Thermal Resistors are used for sensing temperature. The thermal resistors were salvaged from the old laptop battery. I did not know their model number. I measured their resistance with respect to temperatures. If the thermal resistor formed a voltage divider with a normal resistor with the thermal resistor connected to 5V and the normal resistor connected to ground, The divided voltage was linearly proportional to temperature from 0C to 80C within +/-2C. I used this simple linear relationship to calculate the temperature.
Measure temperature at the battery is a safety feature, a few degrees off does not matter.
Fuses
There are 10A fuses at the input, output, and V12 to the battery. Also 2A fuses to V8 and V4. They are for safety.
Battery
My battery banks are built with used 18650 Lithium batteries salvaged from old laptops. Their average capacity is 2200mAH. I tested all of them and marked their capacities on the cells. I discarded all those which were below 2000mAH. Before packaging them into groups, I fully charged all of them to 4.2V and let them sit for a week. Then tested the battery voltages again. Any battery with less than 4.1V will be discarded.
I grouped all the batteries into 3 groups so that the difference in combined capacity for each group is below 1%. I did it by lining up all the batteries from the highest capacity to the lowest capacity. Then picked the highest one to group A, the 2nd highest to group B, the 3rd highest to group C. Then reversed the order by putting the remaining highest capacity to group C, then to groupB, and group A. Then reversed the order again.
Using this approach, I was lucky. The capacity difference between groups was less than 1%.
Maybe it had nothing to do with luck. If somebody knows the answer to the following question, please let me know.
There are 300 batteries, the average capacity is 2200mAH and a standard deviation of 50mAH with Normal Gaussian Distribution. If I separate them randomly into 3 groups, what are my chances in getting a less than 1% difference in total group capacity?
Someone on YouTube showed a computer algorithm to make a perfect grouping. But I lost the link and it was boring to enter all the capacities into the computer.
Step 23: Making Battery Banks
Organizing your 18650 battery holder to the size and form factor you like. It must be multiple of 3 if making 3 batteries in series.
Then inserted the batteries to the holder with 2 banks facing up, and 1 bank facing down. People in YouTube videos put hundreds of batteries into one big battery holder package. I like to put batteries into smaller package sizes. If one of the batteries fails, it is much easier to replace the bad battery. Also I didn’t put the battery holder on the top side. The reason was I couldn’t figure a good way to detect a bad battery once they were all in parallel. Only way I could think of was using a thermal camera or a temperature probe since a bad battery would be hotter than other good batteries. If putting a battery hold on top, it blocked the thermal camera or temperature probe from detecting a bad battery.
I wonder how others detect the bad batteries since they stack thousands of batteries on top of each other. Maybe they are using batteries with infinity life cycles.
Use Nickel Strip to bonded all batteries together with a spot welder. Don’t use soldering iron to solder them. It might damage the battery due to high temperature.
To have fun and save money, I built my own semi-automate spot welder using Arduino.
Step 24: Semi-automatic Spot Welder
This is a simple semi-automatic spot welder.
A good welding requires hundreds of Amperes in a very short duration. If there is strong current for a long duration, it blows the nickel strip into pieces. If the current is weak and duration is long, it oxides the nickel strip surface and creates a weak bonding. Based on my experimental results for a strong bonding is to max out the welding current with the shortest duration.
To test the strength of the bonding, ripple the nickel strip off from the battery with a pair of pliers. If the nickel strip broke off and the welding spot still remains on top of the battery, that is a good welding.
Spot Welder Description
The spot welder has positive and negative welding probes which are made of common household AWG 14 solid copper electrical wires. The probe has a bare copper wire at the center and surrounded by 6 wires with insulation on. The positive probe connects directly to the positive terminal of a 12V car battery and a sense wire connects from the tip of the probe to Arduino A7 voltage divider input. The negative probe is built the same way as the positive probe. It connects to a parallel group of NMOS drains, and then from the NMOS sources to the negative terminal of the battery which is the circuit ground.
The NMOS are RU6888R which I salvaged from a BMS I purchased. Since I was not using the BMS, I ripped out the NMOS for parts. I believe any power NMOS with low ON resistance will be fine.
A car battery is used because it has a lot of cranking power in hundreds of Amperes output.
At first, the microcontroller checks if the positive probe reaches 12V. If it is above 12V, then it is ready to weld. Then the microcontroller monitors voltage at the negative probe which is at 0V due to pull down by R2, R5. When both probes touch the nickel strip, voltage at the negative probe will jump up that means both probes are connected together. Then the microcontroller waits for 0.2 seconds and turns M1 on for a duration that is programmed by the user which ranges from 10mS to 60mS. After that the microcontroller waits for the probes to move away from the nickel strip by sensing the voltage at the negative probe drops back down to 0. Then restart the cycle again.
The duration of welding is important. After welding, check the welding spot. A good welding spot should have a solid round look and the surrounding area has no sign of oxidation. Then test the bonding strength by rippling the strip out with a pair of pliers.
Start with 10mS welding time. If the bonding strength is weak, increase the welding time. But don’t go over 60mS. If over, that means either your battery is weak or the resistance from the battery to the probe are too high. My setup welding time is 30mS..
A7 is for sensing voltage at the positive probe tip.
A6 is for sensing voltage at the negative probe tip.
A5 is for sensing the M1 drain voltage. It is for debugging to check the ON resistance of M1. In the process of building the welder, I stored the voltage profiles of A7, A6,A5 into an array during the welding period with a 2mS time interval.
To minimize the resistance from the battery to the probe, use heavy gauge wires. I used AWG 12 speaker wires.
For detail, read my spot welder Arduino code
For welding, push both probes down to the nickel strip. If you think it is on the wrong spot, lift the probes up fast and wait for a few seconds before pushing the probe back on the nickel strip. During welding, there will be a little spark at the welding spot, and the probe cable will shake.
Attachments
Step 25: Building the Prototype
I used multiple double side prototype boards and linked them together with AWG 20 solid wires to form a bigger piece of prototype board.
I used AWG 18, 22, and 28 silicon insulated wires for interconnects at the back side of the PC board. Silicon wires are flexible and do not melt during soldering.
I believe I provide enough information to allow users with an electronic background to design and make their own solar charger. At least give somebody a basic circuitry background information on solar chargers.
If there is a knowledge gap I miss to present, most likely it is my ignorance on the subject that prevents me from presenting it.
To fill up the knowledge gap, do what I did before by googling it, watching it on YouTube, reading a book or asking a professional. I had nothing to add on to the subject since that is all I learned starting with no knowledge of the solar system.
Step 26: Software Implementation.
I put software in the last section because programming was my weak point. I was a hardware guy not a software person. My code was not elegant and my comments were not sufficient. But it got the job done.
The SolarCharger.ino is the solar charge Arduino codes.
There are 4 main functions for the microcontroller.
1. Controlling the input switch.
It turns the switch ON, if there is enough voltage at the input to charge the batteries which need charging. Otherwise turn it off.
2. Output switch is always ON after boot up.
It disconnect the output switch if there is an over current or under voltage conditions.
3. Adjust the switching pulse width so that maximum power is extracted from the input.
4. Detect which battery bank has the lowest voltage and feeds extra current to that bank.
All other functions are secondary. They are good to have.
Following sections are a brief description of the codes to supplement the insufficient comments in the program.
Attachments
Step 27: Setup()
At setup, he microcontroller enabled the output switch and disable the input switch, then initialized the LCD, NRF24L01, and read and displayed the stored data from the EEPROM. It paused until the user hit the interrupt button to continue. This allowed time for the user to read the display.
After the interrupt button is pressed, the microcontroller does a measurement and runs a self test routine. If the self test routine detects failure from the measured data, the microcontroller opens all switches and saves the measured data to EEPROM at address 0 then stops indefinitely. Need to push the boot button at the microcontroller to restart it.
If no error is detected, the microcontroller saves the measurement to EEPROM at address 32. Then it set the PWM frequency to 20KHz with pulse width at 0. Also reset all variables to 0. With the measurements, the microcontroller sets which battery to balance, and sets input switch ON or OFF.
The reason for address 32 is because the measured data is 32 bytes. Address 0 is assigned to save measurement with error, Address 32 is for saving initial power up measurement or measurement when the interrupt button is pressed again.
Step 28: Loop()
At the beginning of the loop, the microcontroller does a measurement. Then runs a checking on the measurement to detect any fault with the charger. If fault is detected, it opens up all switches , displaying and saving the measurement, then pauses indefinitely.
For every 100 loop cycles, the microcontroller does a charger setup update. It updates the balancing switches, the input switches and adjusted the switching pulse width based on the measurements. Also it broadcasted the measurements through the NRF24L01
One cycle looping time is 14mS, that means there is a setting updated every 1.4 seconds. To make a faster update rate, reduce the number loop counts or do away some secondary functions.
Step 29: Meas()
This function does the voltages and currents measurement and stores the measurement to the data structure vm.
Total required measurements is 30 bytes. NRF24L01 maximum data package size is 32, Two variables RadioId and SendCounts are added in vm to make vm size to 32.
There are three ADCs, ADS1115A, ADS1115B and internal ADC.
The internal ADC is less accurate. It is used for measuring the two thermal resistor voltages, the input voltage and the output voltage.
ADS1115A measures the input current, battery voltages v4, v8 and v12.
ADS1115B measures the charging current, inductor current, Internal input voltage and Vcc
Both ADS1115A and ADS1115B are run in parallel to reduce conversion time.
Step 30: Conv_float()
All the measurement data are integers. This function converts all measurements to floating point numbers.
Most voltages go through a voltage divider and all current go through current sensors to ADC inputs. This function translates the measurements from the input of the ADC to the actual voltages or currents by multiplying them with the conversion coefficients. Then it calculates the input voltage, output voltage, individual battery voltages, voltage difference between batteries, power in and power out. Also It accumulates the current measurement to calculate input and output ampere-hours, and uses them in pulse width setting.
This multiplication coefficient is generated by measuring the actual voltage or current with a DVM and the values printed out by the controller. At the beginning, the actual value and the print out values will be off. By adjusting those coefficients so that they match exactly.
Following are conversion multiplication coefficients.
#define ainm 2.9298/4096.0 //Arduino ADC multiple factor from Vin
#define aoutm 2.6636/4096.0 //Arduino ADC multiple factor from Vout
#define at1m 1.0086/2048.0 // temperature 1
#define at2m 1.0082/2048.0 // temperature 2
#define v12M 3.246 // ADC multiple factor from v12
#define v8M 2.149 // ADC multiple factor from v8
#define v4M 1.08865 // ADC multiple factor from v4
#define ILM 2.5686// ADC multiplication factor for average inductor current
#define IinM 2.5339// ADC multiplication factor for average inductor current
#define IinM 2.614// ADC multiplication factor for average inductor current
#define IoutM 2.4578 // ADC multiplication factor for output current
#define VintrM 4.8441 // ADC multiplication factor for internal input voltage
#define V5M 1.9989 // ADC multiplication factor Vcc, 5V
Step 31: Config_ads1115a() , Config_ads1115b (), Reg_ads1115a (), Reg_ads1115b ()
config_ads1115a() and config_ads1115b () sets up both ADS1115 input and speed.
It initializes a start of conversion on both ADC.
The ADC needs 1.2mS wait time to finish the conversion. By initializing both of them together, that saves time.
reg_ads1115a () and reg_ads1115b () read the ADC converted data.
Step 32: Meas328(), Meas2_adc(), Meas4_adc()
meas328() sets up the Arduino internal ADC input selection and reads the ADC outputs. The ADC input are temperature 1, temperature 2, input voltage and output voltage
meas2_adc() initializes the ADC to convert and read its ADC data 3 times. The first reading is discarded, and returns the sum of the 2 ADC readings. This is for temperature reading.
meas4_adc() initializes the ADC to convert and read its data 5 times. The first reading is discarded, and returns the sum of the 4 ADC readings. This is for input voltage and output voltage. Sum of 4 is for reducing conversion noise.
Step 33: In_sw()
This function controls the input switch on or off.
If input voltage is higher than a threshold voltage and higher than the battery voltage, then the input switch is on. Otherwise it is off.
There is no function implemented to control the output switch. The output switch turns ON at boot up. It only turns off if self_chk() function detects a fault. To manually shut it down is by turning the switch off at the 5V regulator.
Step 34: Balance() and Pw_adj()
balance() set the lowest voltage battery balancing switch ON. Only 1 switch is ON at a time. If the difference in voltage between batteries was less than 10mV, Then all balancing switches are OFF.
pw_adj() set the PWM pulse width. At boot up, the pulse width is zero, By comparing the previous inductor current (sum of 100 measurements) to the present inductor current (sum of 100 measurements). If the present current is higher than the previous current, it increments the pulse width by 2. Otherwise decrement by 1. Due to noise, and the likelihood that increasing pulse width would get more power, so increment is 2 and decrement is 1. Due to delay time to switch on PMOS, if the pulse width is less than 5, the pulse width is increased by 2. When any battery reaches the voltage limit, the pulse width decreases by 3 counts. There is a limit on maximum pulse width. If the pulse width is above the limit, the pulse width is set to the limit.
Step 35: self_chk()
self_chk() checks all measurements with a set of limits. If any measurement is beyond the limits, it is a fault. It opens up all the switches and saves the measurements to EEPROM. Then display the measurement and stop the program indefinitely.
Following are the limits define in the program.
// Limits
#define lmt_Iout 10.0 // Output current limit
#define lmt_Iin 10.0 // Input current limit
#define lmt_temp 60.0 // Temperature limit
#define lmt_intrV 0.8 //Internal input voltage limit. Due to M1 body diode forward voltage, the internal input voltage can’t not be 0.8V lower than V12.
#define INOFF 12.2 // Input voltage threshold to shut off PWM
#define In_ON 12.0 // Input switch ON voltage threshold
#define battery_OV 12.8 // over voltage for combine batteries
#define battery_UV 9.0 // under voltage for combine batteries
#define out_UV 9.0 // under voltage for output
#define Max_unbal 0.2 // maximum unbalance threshold voltage
#define Li_OV 4.3 // maximum Lithium battery OV limit
#define Li_UV 3.3 // minimum Lithium battery UD limit
#define Li_pw_thr 4.2 // Lithium Battery threshold. If exceeded pulse width decreases by 3
Step 36: Prn_results(), Lcd_prn(), RF Broadcasting
prn_results() prints out measurements to the serial monitor.
Lcd_prn() prints out measurements to the LCD display.
There is no function for broadcasting data since it is only 1 line.
if (!_radio.send(DESTINATION_RADIO_ID, &vm, sizeof(vm), NRFLite::NO_ACK)) halt(12);
If broadcast unsuccessful, then stop the program with an error code of 12.
Step 37: Remote Display Module
This module displays measurements which are wirelessly broadcasted from the Solar Charger.
To make the receiving more reliable, placed the NRF24L01 at least a couple of inches from the Arduino. Also adding L1, C1 and C2 extends the communication distance. I am using an Arduino 168p instead of 328p because it is cheaper. 168p is the same as 328p except 168p internal memory is half the size of 328p.
The remote display module has much less coding which does not require a lot of memory. User can still use the 328p instead of 168p.
Arduino code NRFLite_rec.ino , its functions are ported from Solar Charger codes except the RF receiving function.
The RF receiving function is only 1 line.
while (_radio.hasData()) { _radio.readData(&vm); // Reading RF data.
The LCD is 4 rows by 20 columns.
The 1st row displays V12, V8, V4
The 2nd row displays Vin, Vinternal, Vout, and PORTD in HEX
The 3rd row displays Input current, Output current, Pulse Width, Temperature probe 1 and Temperature probe 2
The 4th row displays voltage across battery Vb12, Vb8, mismatch voltage between batteries, and error code.
My battery bank started off with a 150mV voltage difference. After 2 weeks of operation, the difference drops down to 2mV.
The mismatch voltage will jump up and down by more than 30mV due to battery internal resistance, time for lithium ion crossing the boundary, and which battery bank gets the balancing. Also voltages measurements are not done at the same instant. The jump around can be reduced by averaging. This is not a problem if the general trench of mismatch is decreasing and not growing.
Attachments
Step 38: Software Tips
For faster Arduino boot up use ArduinoISP to upload the program to 328p.
File > Examples > 11.ArduinoISP > ArduinoISP The comments in the codes already explained how to do it. For clarification, there are many tutorials posted in YouTube videos showing how to do it.
Using ArduinoISP to burn the boot loader from UNO to NANO, that will increase useful memory space for programming.
Step 39: EPILOGUE
Started with no knowledge, then learned, then put into practice.
That is what my email name represented.
I hope what I showed in my document will be beneficial to somebody.
To make my project successful, I am indebted to a professional engineer, my former colleague, my good friend Kris for over many months who gave me stimulating discussions and encouragement. Finally, I express my special thanks to my mentor and former boss Russ for assistance in proofreading. He had been generous in making many circuit improving suggestions which were so helpful and vital. For example the inductor air gap idea was his.
I have the same document you can download How to make BMS.
Inside that document, the mathematical equations are more readable, and texts are wrap around the pictures so it is easier to follow the explanation.
Thanks for reading my article, and thanks in advance for your comments.
Have fun designing and building.
That's all Folks.