Categories

# Using the ACS712 Current Sensor

I recently obtained a couple of these sensors for a project and have been exploring how they can be used to detected both DC and AC currents. I also wanted to understand what was needed for proper calibration of the ADC readings at the Arduino.

The ACS712 hall effect current sensor is commonly available from generic suppliers incorporated onto GY712 sensor boards shown in picture.

A full sketch with all the code presented in this article can be found at my libraries site.

### Using the Sensor

The GY712 sensor board is easy to use – simply break one of the power supply wires and connect each end to the screw terminals on the board. Connections to the Arduino are the usual GND, V (5V) and Signal output connected to an analog input. Annoyingly it does not use the conventional ‘GVS’ order that simplifies connection to a sensor shield.

The sensor consists of a linear Hall circuit with a copper conduction  path located near the surface of the chip. Applied current flowing through  the copper conduction path generates a magnetic field which the sensor converts into a linearly proportional voltage ±2.5V centered around 2.5V. This voltage is read through the Arduino ADC as a 10 bit number (0-1023), from which the DC or AC current can be calculated.

The voltage returned by the sensor depends on the sensor’s rating. The ±5A sensor will output 185mV for each amp (mV/A), the ±20A 100mV/A and the ±30A 66mV/A. Accurately knowing the voltage is therefore pretty important!

The ACS712 datasheet states that the sensor is “factory-trimmed for accuracy”. This presumably guarantees that the output will be linear.

For a straight line y=mx+b, the linear output (y) needs to be calibrated for zero (b) and for the slope (m) of the line.

The zero adjust is simply the offset from true zero when there is no current flowing through the sensor. Nominally this will be 2.5V, read as the central point of the 0-1023 ADC range (512). A simple way to determine the zero adjust is to take a few readings and average the deviation from 512. This is easiest to do once in the setup() function.

`// Read the analog input a few times to makes sure that we get // a good zero adjustment from nominal center value. There should be no // current flowing while this is happening. for (uint8_t i=1; i<=10; i++) {      uint16_t error = 512 - analogRead(SENSOR_PIN);   sensorZeroAdj = ((sensorZeroAdj * (i-1)) + error)/i; }`

The slope of the line is fixed by the sensor conversion factor (mV/A) but what needs calibration are the ADC values on the x axis. The ADC returns 0 for 0V and 1023 when the input reaches the Arduino supply voltage Vcc (that is, a scaling from 0 through Vcc). Vcc is nominally 5V but in practice it will vary depending on the source of supply and what else is being powered by the Arduino board. Simply assuming Vcc = 5V is inaccurate and becomes more so at the higher end of the range.

To get around this, the Arduino can be calibrated against a stable 1.1V internal reference voltage. This is discussed in many other places (for an example, look here). Using the actual Vcc ensures the accuracy of the resulting conversion.

### Sensing DC Current

DC current flows in the same direction over time, so sensing the current is simply a matter of reading the analog value, applying the calibration parameters, and scaling to the obtain the current reading.

For computational efficiency integer arithmetic is used throughout in the code below. To retain significant digits, the value is calculated in milliamps rather than amps.

`const uint8_t SENS = 100; // sensor value from datasheet in mV/Aint32_t senseCurrent(void){  int32_t sensorValue = analogRead(SENSOR_PIN) + sensorZeroAdj;  int32_t Vcc = readVcc();  int32_t convertedmA = (1000L * (((Vcc*sensorValue)/1024L)-(Vcc/2)))/SENS;  return(convertedmA);}`

### Sensing AC Current

Sensing AC current is slightly more complex as the current reverses direction with each sinusoidal cycle. What we are generally interested in is the Root Mean Square (RMS) voltage Vrms (see RMS voltage of a Sine Wave for a tutorial on this concept).

Vrms can be calculated from the peak voltage Vp of the sine wave as Vrms = Vp √2. Using the Arduino, we can repeatedly sample the voltage to get the highest reading. As the sine wave has a know frequency (50 or 60Hz), sampling for two wave periods should be sufficient. To get better accuracy, the code below samples for both maximum and minimum, with Vp half the difference between the two.

`const uint8_t AC_FREQUENCY = 50; // in Hzconst uint8_t SENS = 100; // sensor value from datasheet in mV/Aint32_t senseCurrent(void){  const uint8_t timeSampling = 2 * (1000 / AC_FREQUENCY);  int32_t convertedmA;  uint16_t sampleMin = 1024, sampleMax = 0;  int32_t Vpp, Vcc = readVcc(); // both in milliVolts  // Collect samples over the time sampling period and  // work out the min/max readings for the waveform  for (uint32_t timeStart = millis(); millis() - timeStart < timeSampling;)  {     int16_t sensorValue = analogRead(SENSOR_PIN) + sensorZeroAdj;     if (sensorValue > sampleMax) sampleMax = sensorValue;     if (sensorValue < sampleMin) sampleMin = sensorValue;  }  // now calculate the current  Vpp = (((sampleMax-sampleMin)/2)*Vcc)/1024L;  convertedmA = (707L * Vpp)/SENS; // 1/srt(2) = 0.7071 = 707.1/1000  return(convertedmA);}`

## 11 replies on “Using the ACS712 Current Sensor” AIRPOPCIEsays:

hello I downloaded the IDE but it does not work. Even by doing this:
According to ASC712 you must reverse:
because the sensor value is greater than 512
Do you have an FDI that operates,
thank you

Like

I am a bit confused by your question and it may be because of the translation:
1. When you say that you downloaded the IDE I understand that you downloaded the IDE from the Arduino website. Is that correct? If so, what is the relevance to this question?
2. What does FDI mean?
3. sensorZeroAdj is an int16. This can be + or -, so the adjustment should add in the correct amount if we use +. Why do you think this is incorrect?

Like AIRPOPCIEsays:

lorsque je dis chargé l’IDE en fait c’est le croquis…. Au repos mon ACS712 donne 505 ( 2.471) sur l’entrée AN0. d’ou la nécessitée d’inverser le signe sinon on obtient des valeurs d’intensité astronomique. Il faudrait rajouter un test dans le setup pour savoir si l’on doit inverser ou pas.
Avez vous un code qui fonctionnerait avec mes valeurs?.Merci
————— Translation ————–
‎when I say loaded the IDE in fact it’s the sketch…. At rest my ACS712 gives 505 (2.471) on the AN0 input. hence the need to reverse the sign otherwise we obtain values of astronomical intensity. It would be necessary to add a test in the setup to know if we should reverse or not. ‎
‎ Do you have a code that would work with my values?. Thank you‎

Like Ivan Sotto Mejicosays:

I’m a student and I’m still quite confused with some terminologies. I was just wondering about the initial value of zeroSensorAdj the first snippet of code. What particular should I initialize sensorZeroAdj with? Thanks.

Like

int16_t sensorZeroAdj = 0; // calculate in setup()

Like Mikesays:

Where do you come up with the formula for the zero adjust?
// a good zero adjustment from nominal center value. There should be no
// current flowing while this is happening.
for (uint8_t i=0; i<10; i++)
{
uint16_t error = 512 – analogRead(SENSOR_PIN);
}

Why cant you just average the error?

Like

This was a while ago for me, but looking at the code we are dealing with integer maths. What I think this is trying to stop is the error being zero because it is usually quite small. Probably could be done differently, so feel free to suggest an alternative for others to use.

Like Mikesays:

Just to give some background, I have some ACS in the field for measuring 3 phase current (one acs for each phase). The problem we’re experiencing is that the ACS returns around 2 amps when there is no load (perhaps due to some magnetic interference) so im trying to see how people are calibrating their sensors.

I was just having trouble understanding your loop sorry. Because the first iteration will divide by 0 which doesn’t make too much sense. i was thinking perhaps I could just average the sum of all errors. I know this is quite an old thread so thanks for the feedback.

Like

Yes, divide by 0 is definitely an issue, and i-1 will be 255 in the first loop. The loop should be for (uint8_t i=1; i<=10; i++). Interesting that this did not show up in practice…

I'll correct the text and the repository. Thanks.

Like Mikesays:

Glad I could help. Just one more thing. I was wondering if you were willing to explain the logic behind your formula(if you remember). sensorZeroAdj = ((sensorZeroAdj * (i-1)) + error)/i;

Assuming it starts at 0, this looks to be weighted somehow? I’m not well versed in math theory so wondering if you could shed some light. Thanks!

Like

It is a running average error. Rather than add up to a total and then divide by the number of items (sum/n), which could overflow the integer variable, this keeps a running average and (re)creates a total every time from the previous (n-1) value, add in the error and adjusts and works out a new average.
When I run this, in the average error is usually very small (< 5), so an alternative is to just do a normal averaging calculation as it is unlikely to overflow the integer. That was not clear before the code was tested and run 'in anger'.

Liked by 1 person