21 March 2021 By Julian Spina 12

Arduino DC current measurement problems

Reading DC current using ACS712 with ±0.1A accuracy

Tags: Automation, IoT, Photovoltaic, Solar, Current

Sooner or later all “micro-programmers” want to measure current with Arduino family boards. It can be quite tricky and many different approaches are available on the Internet showing possible solutions. This article demonstrates a setup to obtain an acceptable error in readings. The used chip is Allegro’s ACS712, the datasheet reports specifications and typical/maximum operating parameters for the ACS712.

The most common problems in using ACS712 chip are floating readings and correcting noise influence. The proposed solution comes from in-depth research based on is based on calculating average from multiple reads and applying an alpha filter to results. Alpha filter is the algorithm used for accomplishing noise smoothening down to an acceptable value.

This article will go through a first testing phase and then shows Arduino code used for tests:

  • First Test : ACS712 with NO LOAD, with and without software filtering
  • Second Test : ACS712 with 2 ampere DC LOAD, with and without software filtering
  • Third Test : ACS712 with 5 ampere DC LOAD, with and without software filtering
  • Notes on board setup and code : Source code compiled in Arduino IDE 1.8.10 and notes

Goal: The goal to achieve is a ±0.1A accuracy on current readings.

Setup

For the test setup these pieces are used:

  • A compatible Arduino UNO R3 board It’s surely possible to repeat the test using other microcontrollers (ESP32, ESP8266, ATtiny). Arduino is powered from USB which is used as serial monitor to gather read values.
  • A 30A ACS712 board, it’s possible to use different versions of ACS712 board (5A or 20A versions).
  • Load is a car light bulb, type H7 55W 12V two pin lamp, a single one for 2A test and two in parallel for 5A test.
  • Measurements are done with RichMeters RM303 with an accuracy of ±0.8%+3. DMM is set in DC ampere mode, with probe inserted in 10A connector.
  • DC Power Source is TechStar PS-305D 30V 5A power supply in CC (constant current) mode.

Boards can be bought from AliExpress using below affiliate links: fast shipping is usually in 15-20 days, normal shipping is usually in 45-60 days.

ProductDescriptionPrice (fast Shipping)Price (normal Shipping)
Arduino UNO R3Buy 5pcs ALIEXPRESSBuy 1 ALIEXPRESS
ACS712 board 30ABuy 1pcs ALIEXPRESSBuy 1 ALIEXPRESS

Connections are straighforward from ACS712 board to Arduino: VCC to Arduino’s 5V pin, GND to arduino’s GND pin and OUT pin to Arduino’s A0 analogic pin.

The light bulb is connected with one pin to positive terminal of DC source. The other pin from the light bulb is connected to one pin of the green connector on ACS712. The second pin on green connector is connected in series with the one cable from the DMM, in series with negative terminal of power source.

First Test: ACS712 with no LOAD

This setup is with no load applied on the ACS712 boards input pins which are left floating. The expected result here is to read 0.0A. That doesn’t happen because of fluctuations caused by external noise and noise in the current signal, which are relevant. The results without applying any kind of smoothing/averaging isn’t consistent. The following graph represents the results based on 1000 consecutive reads (with no delay in between analogue reads and no dummy read). Peaks are well visible.

Figure 1 : 1000 Consecutive reads without any software filtering (no load)

Applying averaging and alpha filter on data readings permits to contain error within the ±0.1A range given as a goal. In figure 2 results are reported for a no-load test on 1000 consecutive iterations. Each iteration does another 1000 analogue reads, for a total of 1 million reads from the sensor, and values are both averaged and alpha-filtered. The maximum measured error is ±0.08A, well within the ±0.1A requirement.

Figure 2 – 1000 Consecutive Iterations of 1000 reads, averaged and alpha-filtered (no load)

Second Test: ACS712 with 2A DC Load

Second test shows behaviour under a small 2A DC load. As visible in following figure 3 noise impact is quite relevant under load, too, and makes resulting data unusable. Peak readings of noise range from nearly +5A to over -8A.

Figure 3 : 1000 Consecutive reads without any software filtering (2A DC load)

Again, we can obtain much better results applying averaging and alpha filter on data readings, containing error within the ±0.1A goal. The minimum value read is 1.93A and maximum is 2.07A.

Figure 4 – 1000 Consecutive Iterations of 1000 reads, averaged and alpha-filtered (2A DC load)

Third Test: ACS712 with 5A DC Load

The third and last test is performed using a 5 ampere DC load and results are consistent with previous tests showing a linear behaviour of ACS712 board. The 1000 consecutive analogue reads, without any software filtering, suffer from noise readings and have peaks ranging from +7A to nearly -8A.

Figure 5 : 1000 Consecutive reads without any software filtering (5A DC load)

Last graphic in figure 6 shows results using a 5A DC load and applying average and alpha filter. Resulting values are much more stable than the 2A test showing a range from 4.97A to 5.04A, confirming the validity of software filtering.

Figure 6 – 1000 Consecutive Iterations of 1000 reads, averaged and alpha-filtered (5A DC load)

Full Code

Code is written for both modes, simple analogue read mode and software corrected mode. The first program simply reads analogue port printing values and without applying any noise reduction. The second program reads analogue port and applies average and alpha filter.

/**
 * ACS712 30A Simple ACS712 analog read (no software correction).
 * Serial port is set to 115200 bit/second for better speed.
 * Values printed are measured in amperes units.
 * @author Julian E.Spina (jes@acca3.it)
 */

//float sensitivity = 185.0f; // Uncomment for 5A board, Output sens. 185mV/A
//float sensitivity = 100.0f; // Uncomment for 20A board, Output sens. 100mV/A
float sensitivity = 66.0f; // Uncomment for 30A board, Output sens. 66mV/A

int adcValue= 0;
const int currentPin = A0;
float adcVoltage = 0.0f;
float currentValue = 0.0f;
int ARDUINO_REF = 5000;
int offsetVoltage = ARDUINO_REF / 2;

void setup() {
  Serial.begin(115200);
  delay(2000);
}

void loop() {
  for (int i=0; i<1000; i++)
    doSimpleRead();
  while(true) ; // Do nothing
}

// Simple reading without any correction
void doSimpleRead() {
  analogRead(currentPin); // Dummy read, doesn't help
  adcValue = analogRead(currentPin);
  adcVoltage = (adcValue / 1024.0) * ARDUINO_REF;
  currentValue = ((adcVoltage - offsetVoltage) / sensitivity);

  Serial.println(currentValue, 2);
}

Following code is the version which calculates average and applies alpha filter on data, giving a result where noise is smoothened down to keep output accuracy in the ±0.1 ampere range.

/**
 * ACS712 30A with ±0.1A accuracy using average and alpha filter.
 * Serial port is set to 115200 bit/second for better speed.
 * Values printed are measured in amperes units.
 * @author Julian E.Spina (jes@acca3.it)
 */

//float sensitivity = 185.0f; // Uncomment this for 5A board, Output sensitivity 185mV/A
//float sensitivity = 100.0f; // Uncomment this for 20A board, Output sensitivity 100mV/A
float sensitivity = 66.0f; // Uncomment this for 30A board, Output sensitivity 66mV/A

int adcValue= 0;
const int currentPin = A0;
float adcVoltage = 0.0f;
float currentValue = 0.0f;

int ARDUINO_REF = 5000;
int offsetVoltage = ARDUINO_REF / 2;
float errorCorrectionAmp = 0.0f; // Set this to use a correcting offset on readings, e.g. -0.15f;
const float alpha = 0.1f; // Can vary this value between 0.1 and 0.9, changes filter impact

void setup() {
  Serial.begin(115200);
  delay(2000);
}

void loop() {
  for (int i=0; i<1000; i++)
    doReadAlphaFilter();
  while(true) ; // Do nothing
}

// Reading and apply software correction
void doReadAlphaFilter() {
   float average = 0.0f, previousValue = 0.0f;

   analogRead(currentPin); // Dummy read, doesn't help

   for(int i = 0; i < 1000; i++) {
      adcValue = analogRead(currentPin);
      adcVoltage = (adcValue / 1024.0f) * ARDUINO_REF;
      currentValue = (((float)(adcVoltage - offsetVoltage)) / sensitivity);

      if (i>0)
        currentValue = alpha * currentValue + (1-alpha) * previousValue;

      average += currentValue;
      previousValue = currentValue;
   }

   Serial.println(average / 1000.0f + errorCorrectionAmp, 2);
}

Notes on board setup and code

Two different ACS712 boards, both 30A version, were used for tests, obtaining similar results. Different approaches were tested in code:

  • Dummy reading from analogue pin before effective reading: no appreciable difference in results;
  • Delay between analogue readings: no appreciable difference in results.

Also a hardware modify was tested on ACS712 board:

  • Extra 1nF ceramic capacitor soldered on C2 capacitor (most external capacitor on board): no appreciable difference in results.

References