Shopping


List All Products


Advanced Search





Lost Password?
Forgot your username?
No account yet? Register

View Cart/Checkout
Your Cart is currently empty.

Newest Products!

Current Sensor 30 Amp
Current Sensor 30 Amp
$8.95


Arduino Pro
Arduino Pro
$19.95


Seeeduino Mega
Seeeduino Mega
$43.00


Who's Online

We have 133 guests online

Welcome to Hacktronics


Hello electronics hackers.  hacktronics.com was started in San Diego, California by embedded computing/electronics enthusiasts. We created this site to help you get the components you need to create your projects fast.  We specialize in the Arduino platform.  Take a look at our Arduino tutorials.
 
 
 
Arduino, Arduino Shields, Arduino Sensors, and Arduino Kits
Arduino Current Sensor
Digg!

Del.icio.us!

This Arduino based current, voltage, and power sensor/meter tutorial was created for hacktronics by Steve Spence. 

arduino power meter

 

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.

Here is the circuit for sensing the battery voltage:

arduino voltage divider

 

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 sent 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.40
24           34    12  2    6.00
36           51    12  1.3 9.23
48           68    12  0.9 13.33

All values are in k ohms.

Solve for Vout to make sure Vout never exceeds 5v

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

I then calculated the multiplier for the ADC,

ADC Multiplier = 12.46 / 778 * (R1/ R2)

Sense that current!

ACS715 Current Sensor Board:

acs714 current sensor

 

acs714 arduino circuit

 

The next step is to track the current flowing, or produced by a source. We are using a ACS715 Hall Effect sensor to track the current being passed.

// read the analog in value:
  sensorValue = analogRead(analogInPin);           
  // convert to milli amps
  outputValue = (((long)sensorValue * 5000 / 1024) - 500 ) * 1000 / 133;
amps = (float) outputValue / 1000;

 

Math alert!!!

To calculate watt (volts * amps), amp hours (amps * hours), and watt hours (watts * hours) requires tracking the time component, and performing a bit of math:

float watts = amps * batteryVoltage;
  
sample = sample + 1;
 
msec = millis();
 
time = (float) msec / 1000.0;
 
totalCharge = totalCharge + amps;
 
averageAmps = totalCharge / sample;
 
ampSeconds = averageAmps*time;

ampHours = ampSeconds/3600;
 
wattHours = batteryVoltage * ampHours;

 

Serial output:

 

arduino power sensor output

 

We can now output the results of the calculations to the serial port using the following code:


  Serial.print("Volts = " );                     
  Serial.print(batteryVoltage);    
  Serial.print("\t Current (amps) = ");    
  Serial.print(amps);
  Serial.print("\t Power (Watts) = "); 
  Serial.print(watts);

  Serial.print("\t Time (hours) = ");
  Serial.print(time/3600);
 
  Serial.print("\t Amp Hours (ah) = ");
  Serial.print(ampHours);
  Serial.print("\t Watt Hours (wh) = ");
  Serial.println(wattHours);

 

LCD Display:

 

arduino power meter LCD

 

Keeping a computer connected all the time is inconvenient, so I added a lcd display to the project.

  lcd.setCursor(0,0);
  lcd.print(batteryVoltage);
  lcd.print(" V ");
  lcd.print(amps);
  lcd.print(" A ");
 
  lcd.setCursor(0,1);
  lcd.print(watts);
  lcd.print(" W ");
  lcd.print(time/3600);
  lcd.print(" H ");
 
  lcd.setCursor(0,2);
  lcd.print(ampHours);
  lcd.print(" Ah ");
  lcd.print(wattHours);
  lcd.print(" Wh ");

 


All the code:

The code, schematics, and photo's along with discussion is available at this URL:

http://tech.groups.yahoo.com/group/arduinohome/files/volt%20amp%20watt%20hour%20meter/ and http://forum.pololu.com/viewtopic.php?f=3&t=5415




#include <LiquidCrystal.h>

/* This sketch describes how to connect a ACS715 Current Sense Carrier
(http://www.hacktronics.com/Sensors/Current-Sensor-30-to-30-Amp/flypage.tpl.html) to the Arduino,
and read current flowing through the sensor.

*/

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

/*

Vcc on carrier board to Arduino +5v
GND on carrier board to Arduino GND
OUT on carrier board to Arduino A0

Insert the power lugs into the loads positive lead circuit,
arrow on carrier board points to load, other lug connects to
power supply positive

Voltage Divider

11.66 from + to A4
4.62k from A4 to Gnd
Ratio 2.5238

*/
int batMonPin = A4;    // input pin for the voltage divider
int batVal = 0;       // variable for the A/D value
float pinVoltage = 0; // variable to hold the calculated voltage
float batteryVoltage = 0;

int analogInPin = A0;  // Analog input pin that the carrier board OUT is connected to
int sensorValue = 0;        // value read from the carrier board
int outputValue = 0;        // output in milliamps
unsigned long msec = 0;
float time = 0.0;
int sample = 0;
float totalCharge = 0.0;
float averageAmps = 0.0;
float ampSeconds = 0.0;
float ampHours = 0.0;
float wattHours = 0.0;
float amps = 0.0;

int R1 = 11660; // Resistance of R1 in ohms
int R2 = 4620; // Resistance of R2 in ohms

float ratio = 0;  // Calculated from R1 / R2

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  lcd.begin(20, 4);
}

void loop() {
 
int sampleBVal = 0;
int avgBVal = 0; 
int sampleAmpVal = 0;
int avgSAV = 0;
 
for (int x = 0; x < 10; x++){ // run through loop 10x

  // read the analog in value:
  sensorValue = analogRead(analogInPin);  
  sampleAmpVal = sampleAmpVal + sensorValue; // add samples together

  batVal = analogRead(batMonPin);    // read the voltage on the divider
  sampleBVal = sampleBVal + batVal; // add samples together
 
  delay (10); // let ADC settle before next sample
}

avgSAV = sampleAmpVal / 10;

  // convert to milli amps
  outputValue = (((long)avgSAV * 5000 / 1024) - 500 ) * 1000 / 133; 
 
/* sensor outputs about 100 at rest.
Analog read produces a value of 0-1023, equating to 0v to 5v.
"((long)sensorValue * 5000 / 1024)" is the voltage on the sensor's output in millivolts.
There's a 500mv offset to subtract.
The unit produces 133mv per amp of current, so
divide by 0.133 to convert mv to ma
         
*/

avgBVal = sampleBVal / 10; //divide by 10 (number of samples) to get a steady reading

  pinVoltage = avgBVal * 0.00610;       //  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. 
                                  
                                    NOTE! .00488 is ideal. I had to adjust
                                    to .00610 to match fluke meter.
                                   
                                    Also, depending on wiring and
                                    where voltage is being read, under
                                    heavy loads voltage displayed can be
                                    well under voltage at supply. monitor
                                    at load or supply and decide.
*/

  ratio = (float)R1 / (float)R2;
  batteryVoltage = pinVoltage * ratio;    //  Use the ratio calculated for the voltage divider
                                          //  to calculate the battery voltage
                                         
                                           
  amps = (float) outputValue / 1000;
  float watts = amps * batteryVoltage;
   
  Serial.print("Volts = " );                      
  Serial.print(batteryVoltage);     
  Serial.print("\t Current (amps) = ");     
  Serial.print(amps); 
  Serial.print("\t Power (Watts) = ");  
  Serial.print(watts);  
 
   
  sample = sample + 1;
 
  msec = millis();
 
  time = (float) msec / 1000.0;
 
  totalCharge = totalCharge + amps;
 
  averageAmps = totalCharge / sample;

  ampSeconds = averageAmps*time;
 
  ampHours = ampSeconds/3600;
 
  wattHours = batteryVoltage * ampHours;
 
  Serial.print("\t Time (hours) = ");
  Serial.print(time/3600);
 
  Serial.print("\t Amp Hours (ah) = ");
  Serial.print(ampHours);
  Serial.print("\t Watt Hours (wh) = ");
  Serial.println(wattHours);
 
    lcd.setCursor(0,0);
    lcd.print(batteryVoltage);
    lcd.print(" V ");
    lcd.print(amps);
    lcd.print(" A ");
 
  lcd.setCursor(0,1);
  lcd.print(watts);
  lcd.print(" W ");
  lcd.print(time/3600);
  lcd.print(" H ");
 
  lcd.setCursor(0,2);
  lcd.print(ampHours);
  lcd.print(" Ah ");
  lcd.print(wattHours);
  lcd.print(" Wh ");
 
  lcd.setCursor(0,3);
  lcd.print(ratio, 5);
  lcd.print("   ");
  lcd.print(avgBVal);
 
  // wait 10 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
  delay(10);                    
}


Happy hacking.


Send feedback on this tutorial here.