ACS715 + Arduino help

I’ve written a simple sketch to retrieve the amount of current flowing through the ACS715. Difficulties are the apparent negative readings for milliamps when low current is flowing, and non zero output when no current is flowing. Serial monitor capture is attached.


/* This sketch describes how to connect a ACS715 Current Sense Carrier 
(https://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 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

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 = ((sensorValue * (5000/1024)) - 500)/0.133;  
/* sensor outputs about 100 at rest. 
Analog read produces a value of 0-1023, equating to 0v to 5v. 
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
          
*/
  // print the results to the serial monitor:
  Serial.print("sensor = " );                       
  Serial.print(sensorValue);      
  Serial.print("\t Current (ma) = ");      
  Serial.println(outputValue);   

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

Hello, KK4HFJ.

We spoke on the phone earlier. Your formula is correct, and it would work in a more Math-friendly environment such as Excel or Mathematica:

outputValue = ((sensorValue * (5000/1024)) - 500)/0.133;

However, there are many quirks about arithmetic in C/C++ that will cause you problems.

When you divide an integer by an integer in C or C++, the result will actually be an integer, rounded down. So “(5000/1024)” is actually 4, which is way off from the value of 4.8828125 that you wanted. Some people resort to floating point arithmetic to fix this, but floating point arithmetic is very expensive on an AVR. A simpler solution is to multiply sensorValue by 5000 and then divide the product by 1024. I think you should cast to “long” (32-bit int) to make sure no overflows happen in the multiplication. So now we have:

outputValue = ( ((long)sensorValue * 5000 / 1024) - 500 )/0.133;

That code should work for you, but because you wrote a floating point literal “0.133”, your program will still be doing floating point arithmetic. An easy way to fix this is to change “/0.133” to " * 1000 / 133 ":

outputValue = ( ((long)sensorValue * 5000 / 1024) - 500 ) * 1000 / 133;

You can point out to your readers that “((long)sensorValue * 5000 / 1024)” has a physical meaning on its own; it’s the voltage on the sensor’s output in millivolts.

–David

That did the trick!


I’m adding volts, watts, amp hours and watt hours next. See http://arduinotronics.blogspot.com/2012/04/monitoring-power-consumption-with.html as the project progresses. I’m a ham radio operator in EmComms (Emergency Communications), and this battery capacity meter will allow us to keep tabs on power consumption and battery capacity in a grid down scenario.

Steve Spence
KK4HFJ
http://arduinotronics.blogspot.com/

The project is finished, and now includes a LCD display. I’m monitoring volts, amps, watts, hours, amp hours, and watt hours. http://www.instructables.com/id/DIY-Amp-Hour-Meter-Arduino/

Replacing the acs715 with a bidirectional acs714, so I can track charging and discharging, and use this as a “gas gauge” for my batteries.

Hello.

Very cool! Thanks for sharing your project with us.

- Ben

The one thing that concerns me is the accuracy of the voltage divider, and it’s constant “wandering”. It’s reading low, and wanders around constantly by a half a volt or so, like there’s noise on the line. Any ideas?

  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


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:

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

- Ben

Excellent, thanks. Will update that and report back.

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

Vout = (R2/(R1+R2))*Vin

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


If you have an input impedance problem, you can buffer the input signal with a single-ended opamp, such as an MCP601 or similar. Tie the output directly to the - terminal as well as the Arduino input pin, and the input to the + terminal.