RobTillaart / ACS712

Arduino library for ACS Current Sensor - 5A, 20A, 30A
MIT License
118 stars 33 forks source link
arduino current sensor

Arduino CI Arduino-lint JSON check GitHub issues

License: MIT GitHub release PlatformIO Registry

ACS712

Library for the ACS712 Current Sensor - 5A, 20A, 30A and compatibles.

Description

The ACS712 is a chip to measure current, both AC or DC. The chip has an analogue output that provides a voltage that is linear with the current. The ACS712 library supports only a built in ADC by means of analogRead().

However since 0.3.4 there is an experimental setADC().

The library has 4 core functions:

The parameter cycles is used to measure multiple cycles and average them.

To measure DC current a single analogRead() with conversion math is sufficient to get a value. To stabilize the signal analogRead() is called at least twice.

To measure AC current a blocking loop for 20 milliseconds (50 Hz, 1 cycle) is run to determine the peak to peak value which is converted to the RMS value. To convert the peak2peak value to RMS one need the so called crest or form factor. This factor depends heavily on the signal form, hence its name. For a perfect sinus the value is sqrt(2)/2 == 1/sqrt(2). See Form factor below.

For a 60 Hz environment the blocking is ~16.7 milliseconds, still pretty long.

The mA_AC_sampling() calculates the average of the sumSquared of many measurements. This function should be used when the form factor is not known.

Note to make precise measurements, the power supply of both the ACS712 and the ADC of the processor should be as stable as possible. That improves the stability of the midpoint and minimizes the noise.

Resolution

Sensor mVperA LSB 10bit LSB 12bit LSB 16bit
5 A 185 26.4 mA 6.6 mA 0.41 mA
20 A 100 48.9 mA 12.2 mA 0.76 mA
30 A 66 74.1 mA 18.5 mA 1.16 mA
getmAPerStep();
mA LSB = (5000 mV / maxADC) / mVperA * 1000.0;
mA LSB = (1000 * 5000 mV) / (maxADC * mVperA);

Although no 16 bit ADC built in are known, it indicates what resolution could be obtained with such an ADC. It triggered the experimental supporting of external ADC's with this library.

Calibration and accuracy

The library has no means to calibrate the output or use an offset. However sort of calibrating can relatively easy be done by using the MultiMap library. MultiMap approaches a non-linear mapping by multiple linear mappings.

See https://github.com/RobTillaart/MultiMap.

Tests

The library is at least confirmed to work with the following boards:

Device Voltage ADC steps Notes
Arduino UNO 5.0V 1024 tested with RobotDyn ACS712 20 A breakout.
Arduino UNO 5.0V 1024 tested with Open-Smart ACS712 5 A breakout.
Arduino NANO 5.0V 1024 #18
ESP32 3.3V 4096 #15
Promicro 5.0V 1024 #15

Please let me know of other working platforms / processors (and failing ones!).

Compatibles

Robodyn has a breakout for the ACS758 - 50 A. - See resolution below. This sensor has versions up to 200 Amps, so use with care!

AllegroMicro offers a lot of different current sensors that might be compatible. These include bidirectional and unidirectional ones. The unidirectional seem to be for DC only.

https://www.allegromicro.com/en/products/sense/current-sensor-ics/current-sensors-innovations

Devices that could be compatible:

ACS720 ACS724 ACS725 ACS732 ACS733 ACS758 ACS772 ACS773 ACS780 ACS781
tested #44
ACS37002 ACS37003 ACS71240 ACS3761X ACS37800 ACS72981
tested

If you have tested a compatible sensor, please share your experiences. (can be done by opening an issue to update documentation)

Resolution ACS758

Not tested, but looks compatible - same formula as above

Sensor mVperA LSB 10bit LSB 12bit LSB 16bit directional
50 A 40 122.2 mA 30.5 mA 1.91 mA bi
50 A 60 81.5 mA 20.3 mA 1.27 mA uni
100 A 20 244.4 mA 61.0 mA 3.81 mA bi
100 A 40 122.2 mA 30.5 mA 1.91 mA uni
150 A 13.3 367.5 mA 91.8 mA 5.74 mA bi
150 A 26.7 183.1 mA 45.7 mA 2.86 mA uni
200 A 10 488.8 mA 122.1 mA 7.63 mA bi
200 A 20 244.4 mA 61.0 mA 3.81 mA uni

Interface

#include ACS712.h

Base

mA_AC_sampling performance trick.

A trick to sample faster is to set the frequency to 2 times the actual frequency so to 100 or 120 Hz. This results in sampling only half a period and the same current will be measured. Advantage is that the function only blocks for ~10 ms @ 50Hz (8.5 @ 60Hz). The drawback is about 4x as many variation. So only use if the performance (or less blocking) is needed.

In a similar way one can increase the accuracy (reducing the variation) by setting the frequency a factor 2 lower (25 and 30 Hz). Drawback is a far longer blocking time.

Use with care!

See - https://github.com/RobTillaart/ACS712/issues/38

Midpoint

The midpoint is the (raw) zero-reference for all current measurements. It is defined in steps of the ADC and is typical around half the maxADC value defined in the constructor. So for a 10 bit ADC a number between 500..525 is most likely.

Since 0.3.0 all midpoint functions return the actual midPoint.

Since version 0.3.0 there is another way to determine the midPoint. One can use the two debug functions. (milliseconds > 20 to get at least a full cycle)

and take the average of these two values. In code:

uint16_t midpoint = ACS.setMidPoint(ACS.getMinimum(20)/2 + ACS.getMaximum(20)/ 2);

See - ACS712_20_AC_midPoint_compare.ino

The ACS712 has a midPoint level that is specified as 0.5 * VCC. So autoMidPoint() can help to detect voltage deviations for the ACS712. The library does not support this yet.

Form factor

The form factor is also known as the crest factor. It is only used for signals measured with mA_AC().

The library has a number of predefined form factors:

definition value approx notes
ACS712_FF_SQUARE 1.0 1.000
ACS712_FF_SINUS 1.0 / sqrt(2) 0.707 default
ACS712_FF_TRIANGLE 1.0 / sqrt(3) 0.577
ACS712_FF_SAWTOOTH 1.0 / sqrt(3) 0.577

It is important to measure the current with a calibrated multimeter and determine / verify the form factor of the signal. This can help to improve the quality of your measurements.

Please let me know if other crest factors need to be added.

Since version 0.3.0 the form factor can be determined by

float formFactor = 2.0 * mA_AC_sampling() / ACS.mA_peak2peak();

See - ACS712_20_determine_form_factor.ino

Noise

Default = 21 mV (datasheet)

Noise on the signal can be reduced by using a low pass (RC) filter. Version 0.3.1 includes experimental code to take two sample and average them. The idea is that ((3 + 5)/2)^2 < (3^2 + 5^2)/2

In theory this should suppress noise levels however more investigation in software noise detection and suppression is needed.

mV per Ampere

Used for both for AC and DC measurements. Its value is defined in the constructor and depends on type sensor used. These functions allow to adjust this setting run-time.

Typical values see "Resolution" section above, and the "voltage divider" section below.

Frequency detection

Experimental functionality for AC signal only!

The minimum frequency of 40 Hz is used to sample for enough time to find the minimum and maximum for 50 and 60 Hz signals. Thereafter the signal is sampled 10 cycles to minimize the variation of the frequency.

The microsAdjust() is to adjust the timing of micros(). This function is only useful if one has a good reference source like a calibrated function generator to find the factor to adjust. Testing with my UNO I got a factor 0.9986.

Current version is experimental and not performance optimized.

setADC (experimental 0.3.4)

The easiest way to implement an external ADC is to make a wrapper function as casting for function pointer is a no go area.

//  set to external ADC - 5 volts 12 bits
ACS.setADC(myAnalogRead, 5.0, 4096);

...

uint16_t myAnalogRead(uint8_t pin)
{
  return MCP.read(pin);  //  assuming MCP is ADC object.
}

To reset to the internal ADC use NULL as function pointer. Be sure to set the parameters of the ADC correctly.

//  reset to internal ADC - 5 volts 10 bits
ACS.setADC(NULL, 5.0, 1023);

Note that the use of an external ADC should meet certain performance requirements, especially for measuring ma-AC().

To 'catch' the peaks well enough one needs at least 2 samples per millisecond (2000 sps) for a 60 Hz signal. That gives 34 samples for 360 degrees => 10.6 degrees, which results in a max deviation of 5.3 degrees from peak => max 0.5% off.

As a 50 Hz signal is a bit slower, 2000 sps would give 40 samples for => 9 degrees, which results in a max deviation of 4.5 degrees from peak => max 0.4% off.

The 16 bit I2C ADS1115 in continuous mode gives max 0.8 samples per millisecond. This will work perfect for high resolution mA-DC() but is not fast enough for doing mA-AC(). It will get an accuracy around ~2%.

The SPI based MCP3202 ao can do up to 100 samples per millisecond at 12 bit. These ADC's are perfect both mA-DC() and mA-AC().

Voltage divider

As per issue #15 in which an ACS712 was connected via a voltage divider to the ADC of an ESP32.

Schema

ACS712 ----[ R1 ]----o----[ R2 ]---- GND
                     |
                     |
                ADC of processor

The voltage divider gave an error of about a factor 2 as all voltages were divided, including the "offset" from the midPoint zero current level.

By adjusting the mV per Ampere with setmVperAmp(float mva) the readings can be corrected for this "voltage divider effect".

Examples:

For a 20 A type sensor, 100 mV/A would be the normal value. After using a voltage divider one need to adjust the mVperAmp.

R1 (ACS) R2 (GND) voltage factor mVperAmp corrected
10200 4745 4745 / (10200 + 4745) = 0.3175 100 * 0.3175 = 31.75
4745 10200 10200 / (10200 + 4745) = 0.6825 100 * 0.6825 = 68.25
10200 9800 9800 / (10200 + 9800) = 0.4900 100 * 0.4900 = 49.00

Note: setting the midPoint correctly is also needed when using a voltage divider.

Disconnect detection

(to be tested)

To detect that the ACS712 is disconnected from the ADC one could connect the analog pin via a pull-down to GND. A pull-up to VCC is also possible. Choose the solution that fits your project best. (Think safety).

mA_DC() and mA_AC_sampling() will report HIGH values (Out of range) when the ACS712 is disconnected. The other - peak2peak based functions - will see this as zero current (min == max).

Schema with PULL-UP.

                 ACS712 OUT
                     |
                     |
   VCC ----[ R1 ]----o       R1 = 1 M ohm.
                     |
                     |
                ADC of processor

The library does not support this "extreme values" detection.

RTOS

The library can be used in an RTOS environment, however a few functions of this library are blocking for relative long times.

In version 0.3.9 the mA_DC() calls yield() between every three calls of analogRead. This is done both for the external and intern ADC to prevent blocking of other threads.

For the mA_AC() and mA_peak2peak() a call to yield() is not desirable as the samples are all needed to make a decent measurement. For the applications that need proper scheduling one should put the sampling of the INA226 at least for AC in a separate thread.

There is no RTOS example. If you have and willing to share you are welcome.

ESPhome

For people who want to use this library for ESPhome, there exists a wrapper class for this ACS712 library.

As I do not have ESPhome know how, please share your experiences. This can be done by an issue.

Operation

The examples show the basic working of the functions.

Future

Must

Should - 0.3.x

Could

Won't (unless requested)

Support

If you appreciate my libraries, you can support the development and maintenance. Improve the quality of the libraries by providing issues and Pull Requests, or donate through PayPal or GitHub sponsors.

Thank you,