Measure AC using Arduino & ACS712 Current Sensor

This tutorial explains how to read alternating current (AC) using Arduino and an ACS712 current sensor. I shall use an Arduino UNO board for demonstration. Learn how to wire Arduino and current sensor with an AC load and use Arduino code to read the RMS value of alternating current.

Root Mean Square (RMS) AC is the effective value that describes the heating or power-delivering capability of AC to an electrical load. In the case of sinusoidal AC, average and RMS current can be calculated easily by knowing the peak value of current in a cycle. However if the AC is not sinusoidal, the calculation of RMS AC requires proper sampling of the waveform. Noise in the AC reading from the ACS712 sensor must also be minimized. The code here uses different techniques to ensure the effect of noise is minimal and allows us to measure low values of current with ease.

Components Required

  • Arduino development board
  • ACS712 current sensor module
  • A suitable electrical load
  • OLED Display (optional)

The ACS712 Current Sensor

The ACS712 is a Hall effect current sensor that has to be connected in series with the load through which the current has to be measured. It can measure both AC and DC. The ACS712 IC is available as breakout board modules as shown below.

The ACS712 module consists of three pins for interfacing: VCC, GND, and OUT. The IC operates at 5V DC which has to be supplied between the VCC and GND pins. The OUT pin outputs a voltage proportional to the current flowing through the terminal block.

The ACS712 IC comes in different packages that support different maximum & minimum currents: ±5 A, ±20 A, and ±30 A. I shall use the 5 Ampere version in this article. The selection of a higher range will affect the accuracy of readings. So choose the sensor according to the maximum current of your application. ACS712 can withstand a maximum voltage of 2400 Volts(RMS).

ACS712 consists of an accurate, low-offset, linear Hall circuit with a copper conduction route close to the die’s surface. The Hall IC transforms the magnetic field created by the applied current passing via this copper conduction route into a proportionate voltage.

When no current flows through ACS712, it gives an output of VCC X 0.5 Volts i.e. for 5 Volts supply, the “Zero Current Output Voltage” is 2.5 Volts. As the current flow turns positive(or negative), the voltage at the output pin varies linearly. This variation, called the “sensitivity”, depends on the variant of the ACS712 IC described in the table below:

ACS712 variantSensitivity
ACS712 ELC-05B185mV/A
ACS712 ELC-20A100mV/A
ACS712 ELC-30A66mV/A
Table: ACS712 IC sensitivity

This feature of the IC can be used to estimate the current flowing across it. For example, in the case of ACS712 ELC-05B, if a current of +2 Amps flows through the IC, the voltage at its output terminal will be: 2.5 Volts + (185mV X 2) = 2.685 Volts. We can measure this voltage using an analog-to-digital converter (ADC) and calculate the current. As Arduino UNO has an inbuilt ADC, we do not need an external ADC such as the ADS1115.

More details can be found in its datasheet.

⚠ WARNING: Handle mains AC with caution. Things to keep in mind:

  • Turn off the power of the AC switch before working.
  • Double-check wiring connections before applying power.
  • If you’re unsure or inexperienced, consult a qualified electrician.

Wiring ACS712 with Arduino UNO

Connect ACS712 with Arduino UNO as shown in the schematic above. The OUT pin of ACS712 is connected to the A0 pin of Arduino. The AC source is your household mains supply (100–240 V). I’ve used a heating element as an electrical load. Choose the load such that at least a few hundred milliamperes or a few amps flow through the circuit.

This is how my setup looked:

Arduino Code to Measure AC using ACS712

As explained earlier, the zero current output voltage of ACS712 is 2.5 Volts when the supply voltage is 5 Volts. But the supply voltage may not be exactly 5 Volts. So first, we read the zero current output voltage at the A0 pin of Arduino to find out the real value of VCC/2.

Read Zero Current Output Voltage

Here is the Arduino code to find the output voltage of ACS712 when no current flows through its terminals:

const int acsPin = A0;         // Pin connected to ACS712 sensor output
const float vRef = 4.94;          // Arduino's operating voltage
const int adcSamples = 100;       // Number of ADC samples

float midValue = 0;           // value of VCC/2 which will be calculated in code, ideally 2.5V
float noiseVariance = 0;      // Variance of the offset noise

void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(acsPin, INPUT_PULLUP);
  Serial.println("Calculating zero current output voltage.");
  Serial.println("Ensure no current is flowing through load...");
  delay(3000);
  
  // Calculate VCC/2 and noise variance
  calculateMidpoint();
  
  // Output the results
  Serial.print("Zero Current Output Voltage: ");
  Serial.println(midValue, 3);
  Serial.print("Noise Variance: ");
  Serial.println(noiseVariance, 6);
}

void loop() {
  while (true);
}

// Function to calculate VCC/2 and noise variance
void calculateMidpoint() {
  float adc_to_volts[adcSamples];
  float sum = 0.0, sumOfSquares = 0.0;

  // Collect ADC samples
  for (int i = 0; i < adcSamples; i++) {
    int rawADC = analogRead(acsPin); // read raw ADC value          
    adc_to_volts[i] = rawADC * (vRef / 1023.0);  // Convert ADC value to voltage
    sum += adc_to_volts[i];
    sumOfSquares += adc_to_volts[i] * adc_to_volts[i];
    delay(10);  // Small delay for stability
  }

  // Calculate the median of the ADC values
  midValue = calculateMedian(adc_to_volts, adcSamples);

  // Calculate variance of the noise
  float mean = sum / adcSamples;
  noiseVariance = (sumOfSquares / adcSamples) - (mean * mean);
}

// Function to calculate the median of an array
float calculateMedian(float arr[], int size) {
  // Sort the array using bubble sort
  for (int i = 0; i < size - 1; i++) {
    for (int j = 0; j < size - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        // Swap elements
        float temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }

  // Calculate and return the median
  if (size % 2 == 0) {
    // Even number of elements
    return (arr[size / 2 - 1] + arr[size / 2]) / 2.0;
  } else {
    // Odd number of elements
    return arr[size / 2];
  }
}Code language: JavaScript (javascript)

The second line of the code is the voltage measured in the 5V pin of Arduino.

const float vRef = 4.94;          // Arduino's operating voltageCode language: JavaScript (javascript)

When I measured the voltage between 5V and the GND pin with a multimeter, it was 4.94 V instead of 5 V. Change this value according to your findings.

When you run this code, no current must flow through the load i.e. keep your load switched off.

To view the output, open the serial monitor in Arduino IDE by navigating to Tools>Serial Monitor.

As evident, instead of the expected voltage of 2.5 Volts, the measured zero current output voltage is 2.487 Volts. These values printed on the serial monitor will be used in our main code.

Main Code

Upload the following code to display the RMS current read by the ACS712 current sensor in the Serial Monitor of Arduino IDE.

const int acsPin = A0;         // Pin connected to ACS712 sensor output
const float sensitivity = 0.185;  // Sensitivity for ACS712-5A (V/A)
const float vRef = 4.94;          // Arduino operating voltage
const int numSamples = 500;       // Number of samples for RMS calculation

// Values from the first sketch
const float midValue = 2.487;           // Replace with measured values
const float noiseVariance = 0.000192;  // Replace with measured values

void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(acsPin, INPUT_PULLUP);
  Serial.println("Starting current measurement...");
}

void loop() {
  float sumOfSquares = 0.0;
  
  // Collect a large number of samples for current measurement
  for (int i = 0; i < numSamples; i++) {
    int rawADC = analogRead(acsPin);                      // Read sensor value
    float voltage = rawADC * (vRef / 1023.0);                // Convert ADC to voltage
    float filteredVoltage = filterNoise(voltage);            // Filter noisy voltage
    float current = (filteredVoltage - midValue) / sensitivity;  // Convert voltage to current
    sumOfSquares += current * current;                       // Accumulate squared current
  }

  // Calculate RMS current with variance correction
  float rmsMeasured = sqrt(sumOfSquares / numSamples);
  float rmsCorrected = sqrt(max(0.0, rmsMeasured * rmsMeasured - noiseVariance / (sensitivity * sensitivity)));

  // Display corrected RMS current
  Serial.print("RMS Current: ");
  Serial.print(rmsCorrected, 3);
  Serial.println(" A");

  delay(1000);  // Delay before the next measurement cycle
}

// Simple low-pass filter to reduce noise
float filterNoise(float value) {
  static float previousValue = 0.0;
  const float alpha = 0.05;  // Smoothing factor (0.0 < alpha < 1.0)
  float filteredValue = alpha * value + (1 - alpha) * previousValue;
  previousValue = filteredValue;
  return filteredValue;
}
Code language: PHP (php)

Since I’ve used the 5A version of the ACS712 current sensor, I used its sensitivity in the code. Change it if you use any other variant.

const float sensitivity = 0.185;  // Sensitivity for ACS712-5A (V/A)Code language: PHP (php)

In this code, change the following lines according to the values you obtained from the first sketch:

const float midValue = 2.487;           // Replace with measured values
const float noiseVariance = 0.000192;  // Replace with measured valuesCode language: JavaScript (javascript)

You can also try experimenting with the smoothing factor in the following line to mitigate noise:

const float alpha = 0.05;  // Smoothing factor (0.0 < alpha < 1.0)Code language: JavaScript (javascript)

The ADC of Arduino takes 500 samples (numSamples = 500) of the voltage output from ACS712. The sampling time of 500 samples is around 100 milliseconds. Time-period of 50 Hz AC is 20 milliseconds. Thus, here we sample multiple waveforms to minimize noise.

Demonstration

Here is the screenshot of the serial monitor showing a current of around 0.4 Amps when a single heating element was used as a load.

Thereafter, I added another AC load to the circuit which increased the current consumption to around 0.7 Amps. Both of these outputs were also verified with a multimeter capable of measuring True RMS AC.

Add an OLED Display

We can easily make a DIY ammeter by adding an OLED display to this project. I’ve added a 0.96-inch SSD1306 OLED display that uses I2C interface.

The schematic below shows the connection between ACS712, Arduino UNO, and OLED display.

Here are the connection details:

Arduino PinOLED Pin
VinVDD
GNDGND
A4SDA
A5SCL

We shall use a library to interface the OLED display. In Arduino IDE, click on Tools>Manage Libraries. Search “Adafruit SSD1306” and install the library. You can read more about interfacing OLED with Arduino in our article: Arduino With SSD1306 OLED Display Using I2C.

After installing the library and wiring the components as described, upload the following code to display the current read by the ACS712 current sensor.

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED display configuration
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define OLED_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

const int acsPin = A0;            // Pin connected to ACS712 sensor output
const float sensitivity = 0.185;  // Sensitivity for ACS712-5A (V/A)
const float vRef = 4.94;          // Arduino operating voltage
const int numSamples = 500;       // Number of samples for RMS calculation

// Values from the first sketch
const float midValue = 2.487;           // Replace with measured voltage
const float noiseVariance = 0.000192;        // Replace with measured value

void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(acsPin, INPUT_PULLUP);
  
  // Initialize the OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
    Serial.println("SSD1306 allocation failed");
    for (;;); // Halt execution if OLED initialization fails
  }
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.display();
  
  Serial.println("Starting current measurement...");
}
void loop() {
  float sumOfSquares = 0.0;
  
  // Collect a large number of samples for current measurement
  for (int i = 0; i < numSamples; i++) {
    int rawADC = analogRead(acsPin);                         // Read sensor value
    float voltage = rawADC * (vRef / 1023.0);                // Convert ADC to voltage
    float filteredVoltage = filterNoise(voltage);            // Filter noisy voltage
    float current = (filteredVoltage - midValue) / sensitivity;  // Convert voltage to current
    sumOfSquares += current * current;                       // Accumulate squared current
  }

  // Calculate RMS current with variance correction
  float rmsMeasured = sqrt(sumOfSquares / numSamples);
  float rmsCorrected = sqrt(max(0.0, rmsMeasured * rmsMeasured - noiseVariance / (sensitivity * sensitivity)));

  // Display corrected RMS current on Serial Monitor
  Serial.print("RMS Current: ");
  Serial.print(rmsCorrected, 3);
  Serial.println(" A");

  // Display corrected RMS current on OLED
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("RMS Current:");
  display.setTextSize(2);
  display.setCursor(0, 20);
  display.print(rmsCorrected, 3);
  display.println(" A");
  display.display();

  delay(1000);  // Delay before the next measurement cycle
}

// Simple low-pass filter to reduce noise
float filterNoise(float value) {
  static float previousValue = 0.0;
  const float alpha = 0.05;  // Smoothing factor (0.0 < alpha < 1.0)
  float filteredValue = alpha * value + (1 - alpha) * previousValue;
  previousValue = filteredValue;
  return filteredValue;
}Code language: PHP (php)

There are a couple of things you might need to change in the code above. Similar to the code described earlier, you may have to change the sensitivity, midValue and noiseVariance.

The I2C address of your OLED display may also be different from mine. To find out the I2C address using Arduino IDE, navigate to File> Examples> Wire> i2c_scanner and run the script. Enter the address displayed in the Serial Monitor in the following line:

#define OLED_ADDRESS 0x3CCode language: CSS (css)

Demonstration

The current was verified using a multimeter that can measure true RMS AC.

Troubleshooting

  • If you are using jumper wires for connection, measure the continuity of each wire as they often fail.

Conclusion

This tutorial explained how to measure AC using Arduino and ACS712 current sensor, which can be used to build a variety of projects such as energy monitoring, fault detection, or automation. With this foundation, you can further enhance your projects by incorporating features like wireless data transmission or visual dashboards.

Also read: LM317 Constant Current Source Circuit – DIY & Theory


Posted

in

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *