amp | watt hour meter

I've built a voltage and current monitor using a ACS715 from Pololu. I'm calculating hours using millis, and total amp hours consumed. millis is a unsigned long, but I'd like to see hours in operation in a format like 1.25 hours, not jumps from 0 - 1 - 2 etc. Photo's, schematics etc. posted at http://tech.groups.yahoo.com/group/arduinohome/.

/* This sketch describes how to connect a ACS715 Current Sense Carrier 
(http://www.pololu.com/catalog/product/1186) to the Arduino, 
and read current flowing through the sensor.



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

*/
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;
float ratio = 3.2;  // Change this to match the MEASURED ration of the circuit
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 time = 0;
int sample = 0;
float totalAmps = 0.0;
float averageAmps = 0.0;
unsigned long hours = 0;
float ampHours = 0.0;

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

void loop() {
  
  // increment sample counter
  
  sample = ++sample;
  
  time = millis();
  
  
  
  // read the analog in value:
  sensorValue = analogRead(analogInPin);            
  // convert to milli amps
  outputValue = (((long)sensorValue * 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
          
*/
 
  
  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
                                          
                                            // print the results to the serial monitor:
  Serial.print("Voltage = " );                       
  Serial.print(batteryVoltage);      
  Serial.print("\t Current (ma) = ");      
  Serial.print(outputValue);  
  Serial.print("\t Power (Watts) = ");   
  float amps = outputValue / 1000.0;
  float watts = amps * batteryVoltage;
  Serial.print(watts);   
  
  
  totalAmps = totalAmps + amps;
  
  averageAmps = totalAmps / sample;
  
  hours = time/3600000;
  
  ampHours = averageAmps * hours;



  Serial.print("\t Time (hours) = ");
  Serial.print(hours);
  
  Serial.print("\t Energy (ah) = ");
  Serial.println(ampHours);
  
  
  // wait 10 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
  delay(10);                     
}

voltage-divider.png

but I'd like to see hours in operation in a format like 1.25 hours, not jumps from 0 - 1 - 2 etc.

It's your code. You are free to do that. You don't need our permission.

Could you be any less helpful? An idea on how to do that, or even a suggestion on some other improvement would have been more appropriate.

Could you be any less helpful?

I could try.

An idea on how to do that

You have hour as an integer, so storing 1.25 in that is not going to work. You want some resolution on time that is less than 1 hour. You need to define what that resolution is. If it is 1/4 hour, then you need to keep track of minutes, not hours. When you need to know how much time has elapsed, you divide minutes by 60 to get hours. You then subtract 60 times the number of hours to get the remainder. Multiply that value by 4 to get quarter hours.

If the time that you have measured is 78 minutes, that's 1 hour and 18 minutes, or 1 hour, 1 quarter hour and 3 minutes. Ignore the minutes. You can decide to round or truncate as you see fit. You might want 85 minutes to be 1 hour and 1 quarter hour or 1 hour and 2 quarter hours.

Oops, that was not being less helpful. I'll try harder in the future.

even a suggestion on some other improvement would have been more appropriate.

You asked a specific question. "Can I do this?" I answered that question. You did not ask how to do it, or for a review of the code, so I didn't answer those unasked questions.

  sample = ++sample;
++sample;

is equivalent to

sample = sample + 1;

So, your code is equivalent to

sample = sample = sample + 1;

A bit of redundancy there, no?

Start by converting the time into minutes.
Divide by 60 to get the hours.
Modulus divide by 60 to get the minutes.
The total time (as a float) would be hours plus (type casted to float) minutes divided by 60.

I'd calculate the charge more directly - at each sample point multiply the current by the time since the last sample, this gives an estimate of the charge that has flowed in this sample period. Add that to the totalCharge variable (you wrongly call this totalAmps).

The ampere-hours value is totalCharge/3600.0 since 1Ah = 3600C.

Thanks for the help. Got it working the way I wanted it. See attached:

/* This sketch describes how to connect a ACS715 Current Sense Carrier 
(http://www.pololu.com/catalog/product/1186) to the Arduino, 
and read current flowing through the sensor.



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

*/
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;
float ratio = 3.2;  // Change this to match the MEASURED ration of the circuit
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;
unsigned long hours = 0;
unsigned long minutes = 0;
float ampHours = 0.0;
float wattHours = 0.0;
float amps = 0.0;

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

void loop() {
  

  
  
  // read the analog in value:
  sensorValue = analogRead(analogInPin);            
  // convert to milli amps
  outputValue = (((long)sensorValue * 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
          
*/
 
  
  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
                                          
                                            // print the results to the serial monitor:
  amps = (float) outputValue / 1000;
  
  Serial.print("Volts = " );                       
  Serial.print(batteryVoltage);      
  Serial.print("\t Current (amps) = ");      
  Serial.print(amps);  
  Serial.print("\t Power (Watts) = ");   
  
  float watts = amps * batteryVoltage;
  Serial.print(watts);   
  
    
  sample = sample + 1;
  
  msec = millis();
  
  totalCharge = totalCharge + amps;
  
  minutes = msec/1000/60;
    
  time = (float) minutes / 60;
  
  ampHours = totalCharge/time;
  
  averageAmps = totalCharge / sample;
  
  ampHours = averageAmps*time;
  
  wattHours = batteryVoltage * ampHours;
  
 



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


  
  
  // wait 10 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
  delay(10);                     
}

New updates, including LCD, and improved voltage divider fixing an impedance mismatch. Also showcased on http://www.instructables.com/id/DIY-Amp-Hour-Meter-Arduino/

#include <LiquidCrystal.h>

/* This sketch describes how to connect a ACS715 Current Sense Carrier 
(http://www.pololu.com/catalog/product/1186) 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

*/
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;
float ratio = 2.4;  // Change this to match the MEASURED ration of the circuit, 12k R1 and 5k R2
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;

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

void loop() {
  

  
  
  // read the analog in value:
  sensorValue = analogRead(analogInPin);            
  // convert to milli amps
  outputValue = (((long)sensorValue * 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
          
*/
 
  
  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
                                          
                                            
  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 ");
  
  
  
  // wait 10 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
  delay(10);                     
}

DIY-Amp-Hour-Meter-Arduino.jpg

I just received the ACS714 bidirectional hall effect sensor from Pololu. I'll be modifying the project to watch ah in and ah out, with a peukert calculation, for estimated capacity remaining.

I'm adding a low voltage disconnect circuit with a hybrid relay (eliminates MOSFET heating and relay arcing) to protect the battery from excessive discharge. When sketch starts, the MOSFET is enabled, then the relay. Upon voltage drop below predermined value, the relay drops, then the MOSFET. Use a SSR for an AC Load. Full instructable at http://www.instructables.com/id/DIY-Amp-Hour-Meter-Arduino/.