For my off-grid Ham Radio and Solar projects, I needed a way to measure volts, amps, watts, amp hours and watt hours. There's a couple of commercial products that can do this, but not with the flexibility I wanted. I designed a Arduino micro-controller based solution that is very extensible. Right now it monitors the above values of attached gear, and I'm thinking about adding web monitoring and a sd card for data collection. Well, let's get started.
Remove these ads by
Signing UpStep 1: Voltage Divider
I've changed the resistor values of the voltage divider to get a better impedence match with the Arduino A/D. Ben from Pololu says:
The ADC on an AVR can't very accurately measure signals with a high output impedance, and your voltage divider definitely counts as high-impedance. From the ATmega328P datasheet:
The ADC is optimized for analog signals with an output impedance of approximately 10 kΩ or less. If such a source is used, the sampling time will be negligible. If a source with higher impedance is used, the sampling time will depend on how long time the source needs to charge the S/H capacitor, with can vary widely. The user is recommended to only use low impedance sources with slowly varying signals, since this minimizes the required charge transfer to the S/H capacitor.
I think you'll have better results if you make your voltage divider resistors smaller, such as 10k and 5k, though this would waste 10 times more power (maybe 1.6 mW for your current configuration vs 17 mW for the new one).
The Arduino can accept up to 5v on a analog input. Our battery voltage can range as high as 17vdc in certain charge cycles, so we designed a voltage divider that would provide 5v at 17v battery voltage, and less at various lower voltages. See http://en.wikipedia.org/wiki/Voltage_divider for more information on Voltage Dividers.
The code to read that value is as follows:
batVal = analogRead(batMonPin); // read the voltage on the divider
pinVoltage = batVal * 0.00488; // Calculate the voltage on the A/D pin
// A reading of 1 for the A/D = 0.0048mV
// if we multiply the A/D reading by 0.00488 then
// we get the voltage on the pin.
batteryVoltage = pinVoltage * ratio; // Use the ratio calculated for the voltage divider
// to calculate the battery voltage
I have 4 possible nominal battery bank voltages. Each battery bank has a higher possible charge voltage during certain charge cycles. I've called this max, and will prevent a voltage of over 5v being presented to the Arduino pin during all charge cycles including equalize.
Solving for R1
R1 = ((R2*Vin)/Vout)-R2
with a R2 of 5k ohms, I get the following values of R1 for 4 battery voltages:
nominal max R1 R2 Ratio
12 17 12 5 2.4
24 34 29 5 5.8
36 51 46 5 9.2
48 68 63 5 12.6
All resistances in k ohms.
If I solve for R2
R2 = R1 / (Vin/Vout - 1)
With an R1 of 12k ohms, I get the following values for the 4 battery voltages:
nominal max R1 R2 Ratio
12 17 12 5 2.40
24 34 12 2 6.00
36 51 12 1.3 9.23
48 68 12 0.9 13.33
All resistances in k ohms.
Solve for Vout to make sure Vout never exceeds 5v (you can install a 5.1v zener to ground to protect the input).
Vout = (R2/(R1+R2))*Vin
More details at http://arduinotronics.blogspot.com/2012/04/voltage-monitor.html
All parts were obtained from Hacktronics.com.
Additional math notes:
I measured the voltage at A4 in respect to gnd, and with a 12.46 Vin, I got a 3.52 Vout. I also reported the actual ADC output of the analogRead by printing avgVal to the LCD, and got 778 out of a max of 1023 (0-1023).
I then calculated the multiplier for the ADC,
ADC Multiplier = 12.46 / 778 * (R1/ R2)
With actual resistance readings of 11.66 KOhms, and 4.62 kOhms for R1 & R2, I got .00635 instead of the .00488 specified. After updating my code, the volt readings match my fluke. Anyone know why?













































Visit Our Store »
Go Pro Today »




I am no electrical engineer, and this is definitely in a language I feel like I'd have to learn to do this! Would love it you could "write it in english" ;) Obviously, there are many here who love the language of such an 'able, but for us eager DIY'rs who've not tackled the likes... just sayin' ;)
I have an idea about why you have that 0.00610 multiplier instead of the ideal 0.0048... your code is only taking the simple ratio of the resistors - it should maybe be
ratio = (float(R1) +float(R2))/ float(R2)? Please let me know if this correct?
I love the instructable. I am using it to create an amp-hour battery gauge for my new-to-me electric car. The car already has a shunt (400A - 50 mV) so I will be using that. I may use an op-amp to boost the mV signal. Then again the 4.8 mV resolution on the arduino might be fine.
but - i'm troubled by non zero outputs when there's no voltage or current on the inputs - display is showing approx 15 volts and just under 15 amps when they should both be zero
confused
The bi-directional ACS714 output is centered on 2.5v - so zero current flow gives 2.5v - and not the 500mv as mentioned here and within the supplied code
Also, the output of the ACS714 is 66mv/A rather than 133mV/A as mentioned here and within the supplied code
hope this helps someone from having a confusing couple of hours as I have just experienced
There are smaller and larger capacity sensors for other applications.
The mA you mention is how much power your arduino can supply to attached devices.
The arduino does not care how many amps your battery can supply.
Thanks for this article. Most likely I'll build something like this laterâ¦
At the moment I'm trying to measure total energy consumption for a battery powered wireless sensor. The sensor uses about 2µA @3V while idle and some mA while sending the data to the central node. Any ideas about how to measure such small values? Your setup doesn't look like it's able to do it.