Pages: [1]   Go Down
Author Topic: amp | watt hour meter  (Read 2322 times)
0 Members and 1 Guest are viewing this topic.
Andrews SC
Offline Offline
Full Member
***
Karma: 0
Posts: 238
If Arduino is not the answer, you are asking the wrong question!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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/.

Code:
/* 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);                    
}


* acs715 output.PNG (60.65 KB, 799x361 - viewed 63 times.)

* current-monitor.png (11.58 KB, 702x348 - viewed 80 times.)

* voltage-divider.png (6.84 KB, 434x358 - viewed 51 times.)
« Last Edit: May 12, 2012, 03:52:19 pm by Steve Spence - KK4HFJ » Logged

Steve Spence - ET/AT/R&D KK4HFJ

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
Logged

Andrews SC
Offline Offline
Full Member
***
Karma: 0
Posts: 238
If Arduino is not the answer, you are asking the wrong question!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Steve Spence - ET/AT/R&D KK4HFJ

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Could you be any less helpful?
I could try.

Quote
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.

Quote
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.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  sample = ++sample;
Code:
++sample;
is equivalent to
Code:
sample = sample + 1;
So, your code is equivalent to
Code:
sample = sample = sample + 1;
A bit of redundancy there, no?
Logged

California
Offline Offline
Faraday Member
**
Karma: 88
Posts: 3364
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

0
Offline Offline
Shannon Member
****
Karma: 200
Posts: 11694
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

[ I won't respond to messages, use the forum please ]

Andrews SC
Offline Offline
Full Member
***
Karma: 0
Posts: 238
If Arduino is not the answer, you are asking the wrong question!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
/* 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);                    
}


* acs715 output.PNG (95.26 KB, 1223x362 - viewed 51 times.)
« Last Edit: May 05, 2012, 02:48:05 pm by Steve Spence - KK4HFJ » Logged

Steve Spence - ET/AT/R&D KK4HFJ

Andrews SC
Offline Offline
Full Member
***
Karma: 0
Posts: 238
If Arduino is not the answer, you are asking the wrong question!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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/

Code:
#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 (23.2 KB, 500x375 - viewed 46 times.)
Logged

Steve Spence - ET/AT/R&D KK4HFJ

Andrews SC
Offline Offline
Full Member
***
Karma: 0
Posts: 238
If Arduino is not the answer, you are asking the wrong question!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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/.


* hybrid-relay.png (11.84 KB, 708x550 - viewed 48 times.)
« Last Edit: May 16, 2012, 01:10:13 pm by Steve Spence - KK4HFJ » Logged

Steve Spence - ET/AT/R&D KK4HFJ

Pages: [1]   Go Up
Jump to: