DIY Amp / Watt Hour Volt Meter - Arduino

If you like this instructable, please vote for it!

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 Up

Step 1: Voltage Divider

UPDATE!

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:

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.

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? Also, voltages fluctuate wildly.

UPDATE:

Improved voltage reading circuit and sketch at AC Volt Meter (works with DC as well). Rock solid voltage measurement, and very accurate.
svendickman says: 1 month ago
I understand that the grounds must be common, but isnt there a risk for different circuits to share a DC ground?

I read the part about the ADC optimization and did not really understand as I do not see any S/H capacitors anywhere. As my load will be a DC-DC converter it wil have a high impedence and thus the lower impedence of the voltage divider will trickle discharge the battery.
sspence (author) in reply to svendickman1 month ago
the capacitors are inside the atmel 328p. No, there is no risk with shared ground.
svendickman says: 1 month ago
Thanks for the article.
I'm setting up a similar project and I have two little questions.
Is there any risk with sharing ground between the battery and the arduino and possibiliy the ground of a DC-5VDC converter?
Also, wouldnt using higher value resistors lower the battery drain of the volt meter?
sspence (author) in reply to svendickman1 month ago
The grounds must be common.

ATMEL says:
" 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."
Schneiderlein says: 2 months ago
Thanks a lot for that great idea. Does anyone already tried to write a small visual basic program that shows the results a little clearer.
sspence (author) in reply to Schneiderlein2 months ago
Visual basic does not run on a arduino, What is not clear?
Schneiderlein in reply to sspence2 months ago
It should be possible to read the things sent by arduino to the serial port. I already made st like a serial monitor in vb but I want vb to write the results in different labels. One for current, one for voltage and so on. Any idea?
sspence (author) in reply to Schneiderlein2 months ago
oh, you are connecting a computer to the arduino and displaying serial output on the pc. yes, you can do this in VB, Processing, php, any language that can read the serial port. I am not interested in having a computer hooked up, although putting a ethernet shield on here and hosting the data on the arduino web server gives you great html creative control for any web browser to read.
Schneiderlein in reply to sspence1 month ago
For everybody who wants to visualize the results on an pc. Logview is a easy to use and cheap posibility. Just use this code instead of the normal serial.print. (This is logview's openformat.)

Serial.print("\$1;1;0;");
Serial.print(batteryVoltage);
Serial.print(";");
Serial.print(amps);
Serial.print(";");
Serial.print(watts);
Serial.print(";0");
Serial.println(13,DEC);
Moonlight27 says: 9 months ago
How can I use this for mains electricity? We use 220v @60Hz here in the PH.
sspence (author) in reply to Moonlight279 months ago
By using the ACS714 bidirectional current sensor, and a 220vac to 12vdc non regulated power pack for voltage monitoring.
Moonlight27 in reply to sspence9 months ago
I'm so sorry how do I do that (sorry complete noob here). I just wan't to record energy consumption (in kWh) from mains. How do I do that "safely". I've read a lot of people saying you should isolate it using an optocoupler but I'm not sure if it still carries analog signal (the voltage). Please help. :(
sspence (author) in reply to Moonlight275 months ago
You "isolate" the mains with a transformer.
sspence (author) in reply to Moonlight279 months ago
Stay tuned, I'll be putting up a ac version of this project in the near future. Current sensors are by definition isolated.
Flooter says: 6 months ago
Nicely done!
But just want to add on top of what author said: I use ACS712 current sensor (5A Max) and it gave me a lot of headache.
First of all, you need to calibrate it inside your program. I do that by doing first 10 or 20 readings, taking the average and using it as "zero" point (of course it is done when no current is flowing through the sensor).
It is necessary because hall-sensor is very very sensitive and could even react to earth magnetic field, so watch out.
Use as good multimeter as you can find to verify current.
After that my average dispersion is about +/- 20 mA (which is 0.02A), at least I hope it is :)

However I don't know why honestly. Here is my code:

int raw1,raw2,raw3;
unsigned long refV1,refV2,refV3;
float raw0,refV0, current;
float x,xx;
int i;

raw0 = 0;
for (i=1;i<=samplesToTake;i++) {
delay(delayBetweenSamples);
}
raw0 = (float)raw0/samplesToTake;
x = raw0;

if (setupCurrentYES == 0 ) {
initialCurrentX = x;
setupCurrentYES = 1;
}

xx = abs(x-initialCurrentX );
current = xx*multiplier;
return current;

}
"multiplier" has been defined in other part but I did that manually, based on numbers that sensor gave me and comparing them to my multimeter value.
So now it equal to 0.0266 but I don't know why because if the sensor gives you 512-1024 readings for the current from 0-5A it does not have much sense.

Anyone may have a clue?

Secondly, when you measure Voltage - 5V is a "theoretical" value.
It is how Arduino built - it uses "reference" value that is supposed to be +5 but it is not always true. It is better to measure real reference voltage (plz google how to do that) and then use this number instead of just 5.
Alternatively you may supply power to your arduino board from other than your PC-USB port source. Some external PSU with 7-12V would be good - doing that you'll get more stable reference voltage.
rrepass jr. says: 9 months ago
I have a small solar array, I am looking at this instructable to build a monitor for it. I'd like to monitor voltage of battery & Amps of incoming juice. Will this get me in that direction?
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' ;)
sspence (author) in reply to rrepass jr.9 months ago
It will do what you are looking to do, and it is in as p[lain English as possible. You need to learn the "language" in order to understand what you are doing. Otherwise, just buy one.
ayasbek says: 10 months ago
Thanks for sharing!

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.
Sean_ork says: 1 year ago
well - i've now managed to bodge together some logging code using the adafruit SD card shield

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
Sean_ork in reply to Sean_ork1 year ago
I'm now unsure if i am seeing the fully updated code here as it as shown here will give inaccurate results if used with the ACS714

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

sspence (author) in reply to Sean_ork1 year ago
This instructable is for the unidirectional acs715. I have not published a instructable for the acs714 as of yet.
Sean_ork in reply to Sean_ork1 year ago
unplugging the SD card resolves the non zero volts - but i'm seeing 2.5v from the current sensor (to gnd) even when it's output pin isnt connected
sspence (author) in reply to Sean_ork1 year ago
Floating inputs won't be zero unless connected to ground through a resistor.
Sean_ork says: 1 year ago
cool project - did you ever get any data logging code running with this ?
sspence (author) in reply to Sean_ork1 year ago
Not yet. Need to get a sdcard attachment.
francisroan says: 1 year ago
i have a question ..!!my battery is 12.6 V and 4 A if i use ur ciruit wouldnt it kill my arduino i mean 4A amps the arduino can only support a few mA...+ do i need to modify the circuit u shown in the pic to use my battery with me arduino?
sspence (author) in reply to francisroan1 year ago
This current sensor converts 0-30 amps of current being sourced to some load (in my case a ham radio) to a 0-5v signal the arduino can read.

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.

thtux says: 1 year ago
Here's a comment for you ;-)

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.
sspence (author) in reply to thtux1 year ago
There are smaller sensors than the 30 amp I'm using. When I first started playing with this sensor, I was monitoring milli amp loads. A lower capacity sensor may have finer resolution. The rest of the project would be the same. See http://www.pololu.com/catalog/product/1185
tigerbomb8 says: 1 year ago
thanks for writing this..... i want to add a voltage/amp meter to my bench power supply.