0.2.8 ACS712

This commit is contained in:
rob tillaart 2022-08-28 09:44:41 +02:00
parent 459bb84ac2
commit 5ab7ac8d21
18 changed files with 698 additions and 133 deletions

View File

@ -7,5 +7,5 @@ compile:
# - leonardo
- m4
- esp32
# - esp8266
- esp8266
# - mega2560

View File

@ -1,7 +1,7 @@
//
// FILE: ACS712.cpp
// AUTHOR: Rob Tillaart, Pete Thompson
// VERSION: 0.2.7
// VERSION: 0.2.8
// DATE: 2020-08-02
// PURPOSE: ACS712 library - current measurement
//
@ -11,6 +11,7 @@
// 0.1.2 2020-03-21 automatic form factor test
// 0.1.3 2020-05-27 fix library.json
// 0.1.4 2020-08-02 Allow for faster processors
//
// 0.2.0 2020-08-02 Add autoMidPoint
// 0.2.1 2020-12-06 Add Arduino-CI + readme + unit test + refactor
// 0.2.2 2021-06-23 support for more frequencies.
@ -22,102 +23,236 @@
// 0.2.7 2022-08-10 change mVperAmp to float
// add ACS712_FF_SAWTOOTH
// update readme.md + unit test + minor edits
// 0.2.8 2022-08-19 prepare for 0.3.0
// Fix #21 FormFactor
// add mA_AC_sampling() as method to determine
// current when FormFactor is unknown.
// added float _AmperePerStep cached value.
// added getAmperePerStep();
// moved several functions to .cpp
// improve documentation
//
#include "ACS712.h"
// CONSTRUCTOR
ACS712::ACS712(uint8_t analogPin, float volts, uint16_t maxADC, float mVperAmpere)
{
_pin = analogPin;
_mVperStep = 1000.0 * volts / maxADC; // 1x 1000 for V -> mV
_mVperAmpere = mVperAmpere;
_formFactor = ACS712_FF_SINUS;
_midPoint = maxADC / 2;
_noisemV = 21; // Noise is 21mV according to datasheet
_pin = analogPin;
_mVperStep = 1000.0 * volts / maxADC; // 1x 1000 for V -> mV
_mVperAmpere = mVperAmpere;
_AmperePerStep = _mVperStep / _mVperAmpere;
_formFactor = ACS712_FF_SINUS;
_midPoint = maxADC / 2;
_noisemV = ACS712_DEFAULT_NOISE; // 21mV according to datasheet
}
int ACS712::mA_AC(float frequency)
// MEASUREMENTS
int ACS712::mA_AC(float frequency, uint16_t cycles)
{
uint16_t period = round(1000000UL / frequency);
uint16_t samples = 0;
uint16_t zeros = 0;
int _min, _max;
_min = _max = analogRead(_pin);
if (cycles == 0) cycles = 1;
float sum = 0;
// remove expensive float operation from loop.
uint16_t zeroLevel = round(_noisemV/_mVperStep);
uint32_t start = micros();
while (micros() - start < period) // UNO ~180 samples...
for (uint16_t i = 0; i < cycles; i++)
{
samples++;
int val = analogRead(_pin);
// determine extremes
if (val < _min) _min = val;
else if (val > _max) _max = val;
// count zeros
if (abs(val - _midPoint) <= zeroLevel ) zeros++;
}
int point2point = (_max - _min);
uint16_t samples = 0;
uint16_t zeros = 0;
// automatic determine _formFactor / crest factor
float D = 0;
float FF = 0;
// TODO uint32_t math? (zeros * 40) > samples
if (zeros > samples * 0.025) // more than 2% zero's
{
D = 1.0 - (1.0 * zeros) / samples; // % SAMPLES NONE ZERO
FF = sqrt(D) * ACS712_FF_SINUS; // ASSUME NON ZERO PART ~ SINUS
}
else // # zeros is small => D --> 1 --> sqrt(D) --> 1
{
FF = ACS712_FF_SINUS;
}
_formFactor = FF;
int _min, _max;
_min = _max = analogRead(_pin);
// find minimum and maximum and count the zero-level "percentage"
uint32_t start = micros();
while (micros() - start < period) // UNO ~180 samples...
{
samples++;
int val = analogRead(_pin);
// determine extremes
if (val < _min) _min = val;
else if (val > _max) _max = val;
// count zeros
if (abs(val - _midPoint) <= zeroLevel ) zeros++;
}
int peak2peak = _max - _min;
// automatic determine _formFactor / crest factor
float D = 0;
float FF = 0;
// TODO uint32_t math? (zeros * 40) > samples
if (zeros > samples * 0.025) // more than 2% zero's
{
D = 1.0 - (1.0 * zeros) / samples; // % SAMPLES NONE ZERO
FF = sqrt(D) * _formFactor; // ASSUME NON ZERO PART ~ SINUS
}
else // # zeros is small => D --> 1 --> sqrt(D) --> 1
{
FF = _formFactor;
}
// value could be partially pre-calculated: C = 1000.0 * 0.5 * _mVperStep / _mVperAmpere;
// return 1000.0 * 0.5 * peak2peak * _mVperStep * _formFactor / _mVperAmpere);
sum += peak2peak * FF;
}
float mA = 500.0 * sum * _AmperePerStep/ cycles;
// value could be partially pre-calculated: C = 1000.0 * 0.5 * _mVperStep / _mVperAmpere;
// return 1000.0 * 0.5 * point2point * _mVperStep * _formFactor / _mVperAmpere);
float mA = (500.0 * point2point) * _mVperStep * _formFactor / _mVperAmpere;
return round(mA);
}
int ACS712::mA_DC()
float ACS712::mA_AC_sampling(float frequency, uint16_t cycles)
{
// read twice to stabilize the ADC
uint32_t period = round(1000000UL / frequency);
if (cycles == 0) cycles = 1;
float sum = 0;
// float noiseLevel = _noisemV/_mVperStep;
for (uint16_t i = 0; i < cycles; i++)
{
uint16_t samples = 0;
float sumSquared = 0;
uint32_t start = micros();
while (micros() - start < period)
{
samples++;
float current = ((int)analogRead(_pin)) - _midPoint;
sumSquared += (current * current);
// if (abs(current) > noiseLevel)
// {
// sumSquared += (current * current);
// }
}
sum += sqrt(sumSquared / samples);
}
float mA = 1000.0 * sum * _AmperePerStep / cycles;
return mA;
}
int ACS712::mA_DC(uint16_t cycles)
{
// read at least twice to stabilize the ADC
analogRead(_pin);
int steps = analogRead(_pin) - _midPoint;
float mA = 1000.0 * steps * _mVperStep / _mVperAmpere;
if (cycles == 0) cycles = 1;
float sum = 0;
for (uint16_t i = 0; i < cycles; i++)
{
sum += analogRead(_pin) - _midPoint;
}
float mA = 1000.0 * sum * _AmperePerStep / cycles;
return round(mA);
}
// CALIBRATION MIDPOINT
void ACS712::setMidPoint(uint16_t midPoint)
{
_midPoint = midPoint;
};
uint16_t ACS712::getMidPoint()
{
return _midPoint;
};
void ACS712::incMidPoint()
{
_midPoint += 1;
};
void ACS712::decMidPoint()
{
_midPoint -= 1;
};
// configure by sampling for 2 cycles of AC
// Also works for DC as long as no current flowing
// note this is blocking!
void ACS712::autoMidPoint(float frequency)
void ACS712::autoMidPoint(float frequency, uint16_t cycles)
{
uint16_t twoPeriods = round(2000000UL / frequency);
uint32_t total = 0;
uint32_t samples = 0;
uint32_t start = micros();
while (micros() - start < twoPeriods)
if (cycles == 0) cycles = 1;
uint32_t total = 0;
for (uint16_t i = 0; i < cycles; i++)
{
uint16_t reading = analogRead(_pin);
total += reading;
samples++;
// Delaying ensures we won't overflow since we'll perform a maximum of 40,000 reads
delayMicroseconds(1);
uint32_t subTotal = 0;
uint32_t samples = 0;
uint32_t start = micros();
while (micros() - start < twoPeriods)
{
uint16_t reading = analogRead(_pin);
subTotal += reading;
samples++;
// Delaying ensures we won't overflow since we'll perform a maximum of 40,000 reads @ 50 Hz.
delayMicroseconds(1);
}
total += (subTotal/samples);
}
_midPoint = total / samples;
_midPoint = total / cycles;
}
// Experimental frequency detection.
// CALIBRATION FORM FACTOR
void ACS712::setFormFactor(float formFactor)
{
_formFactor = formFactor;
};
float ACS712::getFormFactor()
{
return _formFactor;
};
// CALIBRATION NOISE
// noise defaults 21 datasheet
void ACS712::setNoisemV(uint8_t noisemV)
{
_noisemV = noisemV;
};
uint8_t ACS712::getNoisemV()
{
return _noisemV;
};
// CALIBRATION mV PER AMP
// Adjusting resolution AC and DC
void ACS712::setmVperAmp(float mVperAmpere)
{
_mVperAmpere = mVperAmpere;
_AmperePerStep = _mVperStep / _mVperAmpere;
};
float ACS712::getmVperAmp()
{
return _mVperAmpere;
};
float ACS712::getAmperePerStep()
{
return _AmperePerStep;
};
// Frequency detection.
// uses oversampling and averaging to minimize variation
// blocks for substantial amount of time, depending on minimalFrequency
float ACS712::detectFrequency(float minimalFrequency)
@ -163,6 +298,17 @@ float ACS712::detectFrequency(float minimalFrequency)
return frequency;
}
// CALIBRATION TIMING
void ACS712::setMicrosAdjust(float factor)
{
_microsAdjust = factor;
};
float ACS712::getMicrosAdjust()
{
return _microsAdjust;
};
// -- END OF FILE --

View File

@ -2,7 +2,7 @@
//
// FILE: ACS712.h
// AUTHOR: Rob Tillaart, Pete Thompson
// VERSION: 0.2.7
// VERSION: 0.2.8
// DATE: 2020-08-02
// PURPOSE: ACS712 library - current measurement
//
@ -12,7 +12,7 @@
#include "Arduino.h"
#define ACS712_LIB_VERSION (F("0.2.7"))
#define ACS712_LIB_VERSION (F("0.2.8"))
// ACS712_FF_SINUS == 1.0/sqrt(2) == 0.5 * sqrt(2)
@ -22,6 +22,9 @@
#define ACS712_FF_TRIANGLE (1.0/sqrt(3))
#define ACS712_FF_SAWTOOTH (1.0/sqrt(3))
#define ACS712_DEFAULT_FREQ 50
#define ACS712_DEFAULT_NOISE 21
class ACS712
{
@ -40,57 +43,74 @@ class ACS712
// returns mA
// blocks 20-21 ms to sample a whole 50 or 60 Hz period.
// works with peak2peak level and Form Factor.
// lower frequencies block longer.
int mA_AC(float frequency = 50);
int mA_AC(float frequency = ACS712_DEFAULT_FREQ, uint16_t cycles = 1);
// returns mA
// blocks 20-21 ms to sample a whole 50 or 60 Hz period.
// works with sampling.
// lower frequencies block longer.
float mA_AC_sampling(float frequency = ACS712_DEFAULT_FREQ, uint16_t cycles = 1);
// returns mA
// blocks < 1 ms
int mA_DC();
int mA_DC(uint16_t samples = 1);
// midPoint ADC for DC only
void setMidPoint(uint16_t midPoint) { _midPoint = midPoint; };
uint16_t getMidPoint() { return _midPoint; };
void incMidPoint() { _midPoint++; };
void decMidPoint() { _midPoint--; };
// midPoint functions
// set reference point for both DC and AC
void setMidPoint(uint16_t midPoint);
uint16_t getMidPoint();
void incMidPoint();
void decMidPoint();
// Auto midPoint, assuming zero DC current or any AC current
void autoMidPoint(float frequency = 50);
void autoMidPoint(float frequency = ACS712_DEFAULT_FREQ, uint16_t cycles = 1);
// Form Factor is also known as crest factor;
// affects mA_AC() only, default sinus.
void setFormFactor(float formFactor = ACS712_FF_SINUS) { _formFactor = formFactor; };
float getFormFactor() { return _formFactor; };
void setFormFactor(float formFactor = ACS712_FF_SINUS);
float getFormFactor();
// noise defaults 21
void setNoisemV(uint8_t noisemV = 21) { _noisemV = noisemV; };
uint8_t getNoisemV() { return _noisemV; };
// noise defaults 21 datasheet
void setNoisemV(uint8_t noisemV = ACS712_DEFAULT_NOISE);
uint8_t getNoisemV();
// AC and DC
void setmVperAmp(float mVperAmpere) { _mVperAmpere = mVperAmpere; };
float getmVperAmp() { return _mVperAmpere; };
// Adjusting resolution AC and DC
void setmVperAmp(float mVperAmpere);
float getmVperAmp();
float getAmperePerStep();
// Frequency detection.
// the minimal frequency determines the time to sample.
float detectFrequency(float minimalFrequency = 40);
void setMicrosAdjust(float factor = 1.0) { _microsAdjust = factor; };
float getMicrosAdjust() { return _microsAdjust; };
void setMicrosAdjust(float factor = 1.0);
float getMicrosAdjust();
private:
uint8_t _pin;
float _mVperStep;
float _formFactor; // point2point -> RMS
float _formFactor; // peak2peak -> RMS
float _mVperAmpere;
uint16_t _midPoint;
float _AmperePerStep;
int _midPoint;
uint8_t _noisemV;
float _microsAdjust = 1.0; // 0.9986
};
// simulate analogRead() - develop only -
// static int aRead(uint8_t pin)
// {
// float t = micros();
// float value = 515 + 50 * sin(t * PI / 180.0);
// return value;
// }
// -- END OF FILE --

View File

@ -1,7 +1,7 @@
//
// FILE: ACS712_20_AC.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// PURPOSE: demo AC measurement with point to point
// URL: https://github.com/RobTillaart/ACS712
@ -22,7 +22,10 @@ ACS712 ACS(A0, 5.0, 1023, 100);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
Serial.print("MidPoint: ");

View File

@ -1,7 +1,7 @@
//
// FILE: ACS712_20_AC_DEMO.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo to set midpoint and mVperAmpere for class.
// PURPOSE: demo to set the mVperAmpere and Form FActor.
// URL: https://github.com/RobTillaart/ACS712
@ -22,7 +22,11 @@ ACS712 ACS(A0, 5.0, 1023, 100);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
}

View File

@ -0,0 +1,52 @@
//
// FILE: ACS712_20_AC_SAMPLING_DEMO.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo to sample AC current and set mVPerAmpere
// URL: https://github.com/RobTillaart/ACS712
#include "ACS712.h"
// Arduino UNO has 5.0 volt with a max ADC value of 1023 steps
// ACS712 5A uses 185 mV per A
// ACS712 20A uses 100 mV per A
// ACS712 30A uses 66 mV per A
ACS712 ACS(A0, 5.0, 1023, 100);
// ESP 32 example (might requires resistors to step down the logic voltage)
// ACS712 ACS(25, 3.3, 4095, 185);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
}
void loop()
{
float mA = ACS.mA_AC_sampling();
Serial.println(mA, 1);
while (Serial.available() > 0)
{
char c = Serial.read();
if (c == '*') ACS.setmVperAmp(ACS.getmVperAmp() + 1);
if (c == '/') ACS.setmVperAmp(ACS.getmVperAmp() - 1);
Serial.print("mVperAmp:\t");
Serial.println(ACS.getmVperAmp());
}
delay(250);
}
// -- END OF FILE --

View File

@ -0,0 +1,59 @@
//
// FILE: ACS712_20_AC_average.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo AC measurement with point to point + averaging
// URL: https://github.com/RobTillaart/ACS712
#include "ACS712.h"
// Arduino UNO has 5.0 volt with a max ADC value of 1023 steps
// ACS712 5A uses 185 mV per A
// ACS712 20A uses 100 mV per A
// ACS712 30A uses 66 mV per A
ACS712 ACS(A0, 5.0, 1023, 100);
// ESP 32 example (might requires resistors to step down the logic voltage)
// ACS712 ACS(25, 3.3, 4095, 185);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
Serial.print("MidPoint: ");
Serial.println(ACS.getMidPoint());
Serial.print("Noise mV: ");
Serial.println(ACS.getNoisemV());
}
void loop()
{
float average = 0;
uint32_t start = millis();
for (int i = 0; i < 100; i++)
{
// select sppropriate function
// average += ACS.mA_AC_sampling();
average += ACS.mA_AC();
}
float mA = average / 100.0;
uint32_t duration = millis() - start;
Serial.print("Time: ");
Serial.print(duration);
Serial.print(" mA: ");
Serial.println(mA);
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,50 @@
//
// FILE: ACS712_20_AC_compare.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo AC measurement comparison
// URL: https://github.com/RobTillaart/ACS712
#include "ACS712.h"
// Arduino UNO has 5.0 volt with a max ADC value of 1023 steps
// ACS712 5A uses 185 mV per A
// ACS712 20A uses 100 mV per A
// ACS712 30A uses 66 mV per A
ACS712 ACS(A0, 5.0, 1023, 100);
// ESP 32 example (might requires resistors to step down the logic voltage)
// ACS712 ACS(25, 3.3, 4095, 185);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
Serial.print("MidPoint: ");
Serial.print(ACS.getMidPoint());
Serial.print(". Noise mV: ");
Serial.println(ACS.getNoisemV());
}
void loop()
{
int m1 = ACS.mA_AC();
float m2 = ACS.mA_AC_sampling();
Serial.print("mA:\t");
Serial.print(m1);
Serial.print("\t\t");
Serial.println(m2);
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,64 @@
//
// FILE: ACS712_20_AC_low_pass.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo AC measurement with point to point with low pass filter
// URL: https://github.com/RobTillaart/ACS712
#include "ACS712.h"
// Arduino UNO has 5.0 volt with a max ADC value of 1023 steps
// ACS712 5A uses 185 mV per A
// ACS712 20A uses 100 mV per A
// ACS712 30A uses 66 mV per A
ACS712 ACS(A0, 5.0, 1023, 185);
// ESP 32 example (might requires resistors to step down the logic voltage)
// ACS712 ACS(25, 3.3, 4095, 185);
float value = 0;
float weight = 0.2;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
Serial.print("MidPoint: ");
Serial.println(ACS.getMidPoint());
Serial.print("Noise mV: ");
Serial.println(ACS.getNoisemV());
Serial.print("Amp/Step: ");
Serial.println(ACS.getAmperePerStep(), 4);
value = ACS.mA_AC(); // get good initial value
}
void loop()
{
// select sppropriate function
float mA = ACS.mA_AC_sampling();
// float mA = ACS.mA_AC();
value += weight * (mA - value); // low pass filtering
Serial.print("weight: ");
Serial.print(weight);
Serial.print(" value: ");
Serial.print(value, 0);
Serial.print(" mA: ");
Serial.print(mA);
Serial.println();
delay(1000);
}
// -- END OF FILE --

View File

@ -1,9 +1,10 @@
//
// FILE: ACS712_20_DC.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// PURPOSE: demo to measure mA DC
// URL: https://github.com/RobTillaart/ACS712
// use with Arduino Serial Plotter
#include "ACS712.h"
@ -22,8 +23,13 @@ ACS712 ACS(A0, 5.0, 1023, 100);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
// Serial.println(ACS.getMidPoint());
}
@ -31,8 +37,8 @@ void loop()
{
int mA = ACS.mA_DC();
Serial.println(mA);
delay(100);
}
// -- END OF FILE --

View File

@ -1,7 +1,7 @@
//
// FILE: ACS712_20_DC_DEMO.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo to set midpoint and mVperAmpere for class.
// PURPOSE: demo to set the midPoint and the mVperAmpere.
// URL: https://github.com/RobTillaart/ACS712
@ -22,7 +22,11 @@ ACS712 ACS(A0, 5.0, 1023, 100);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
ACS.autoMidPoint();
}

View File

@ -1,7 +1,7 @@
//
// FILE: ACS712_detectFrequency.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// PURPOSE: demo detect frequency + timing indication.
// URL: https://github.com/RobTillaart/ACS712
@ -23,7 +23,10 @@ uint32_t start, stop;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
Serial.print("ACS712_LIB_VERSION: ");
Serial.println(ACS712_LIB_VERSION);
}
@ -38,6 +41,7 @@ void loop()
Serial.print(stop - start);
Serial.print("\t");
Serial.println(frequency, 1);
delay(100);
}

View File

@ -0,0 +1,55 @@
// FILE: RMS_by_sampling.ino
// AUTHOR: Rob Tillaart
// DATE: 2022-08-15
// PURPOSE: demo RMS by sampling for AC current
// URL: https://github.com/RobTillaart/ACS712
//
// stand alone sketch to be used with a ACS712
#include "Arduino.h"
// #include "printHelpers.h"
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
}
void loop()
{
// 50 Hz, Arduino UNO, 20A ACS
float current = mA_AC_sampling(50, 5000.0 / 1023, 100, A0);
Serial.println(current);
delay(1000);
}
float mA_AC_sampling(float frequency, float mVperStep, float mVperAmpere, uint8_t analogPin)
{
uint16_t midpoint = 511;
uint32_t period = round(1000000UL / frequency);
uint16_t samples = 0;
float sumSquared = 0;
uint32_t start = micros();
while (micros() - start < period)
{
samples++;
float current = analogRead(analogPin) - midpoint;
sumSquared += (current * current);
}
// Serial.print(scieng(sumSquared, 1, 3));
// Serial.print("\t");
// Serial.print(samples);
// Serial.print("\t");
float AmperePerStep = mVperStep / mVperAmpere;
float RMS = sqrt(sumSquared / samples) * AmperePerStep;
return RMS * 1000.0; // mA
}
// -- END OF FILE --

View File

@ -6,11 +6,13 @@ ACS712 KEYWORD1
# Methods and Functions (KEYWORD2)
mA_AC KEYWORD2
mA_DC KEYWORD2
mA_AC_sampling KEYWORD2
setMidPoint KEYWORD2
getMidPoint KEYWORD2
incMidPoint KEYWORD2
decMidPoint KEYWORD2
autoMidPoint KEYWORD2
setFormFactor KEYWORD2
getFormFactor KEYWORD2
@ -25,6 +27,8 @@ detectFrequency KEYWORD2
setMicrosAdjust KEYWORD2
getMicrosAdjust KEYWORD2
getAmperePerStep KEYWORD2
# Constants (LITERAL1)
ACS712_LIB_VERSION LITERAL1

View File

@ -21,7 +21,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/ACS712.git"
},
"version": "0.2.7",
"version": "0.2.8",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=ACS712
version=0.2.7
version=0.2.8
author=Rob Tillaart <rob.tillaart@gmail.com>, Pete Thompson <pete.thompson@yahoo.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=ACS712 library for Arduino.

View File

@ -16,34 +16,57 @@ Library for the ACS712 Current Sensor - 5A, 20A, 30A.
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()**.
There are 2 core functions:
There are 3 core functions:
- **int mA_DC()**
- **int mA_AC(float freq = 50)** The frequency can be set to typically 50 or 60 Hz
however other values e.g. 50.1 or 40 or 123.456 are possible.
- **int mA_DC(cycles = 1)**
- **int mA_AC(frequency = 50, cycles = 1)** The frequency can be set to any
value but typically to 50 or 60 Hz.
- **float mA_AC_sampling(frequency = 50, cycles = 1)**
To measure DC current a single **analogRead()** with conversion maths is sufficient to get
a value. To stabilize the signal **analogRead()** is called twice.
The parameter cycles is used to do measure multiple cycles and average them.
To measure AC current **a blocking loop for 20 milliseconds** (50 Hz assumed) is run
to determine the peak to peak value which is converted to the RMS value.
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.
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.
See **Form factor** below.
Note to make precise measurements, the power of both the ACS712 and the ADC of
the processor should be as stable as possible.
That would improve the stability of the midpoint and minimizes the noise.
#### Compatibles
To investigate.
Robodyn has breakout for ACS758 - 50A,
Allegromicro offer a lot of different ones, that might be compatible.
https://www.allegromicro.com/en/products/sense/current-sensor-ics/current-sensors-innovations
If you have tested a compatible sensor, please share your experiences.
(can be done by opening an issue to update documentation)
#### Tests
The library is at least confirmed to work with:
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
| ESP32 | 3.3V | 4096 | #15
| Promicro | 5.0V | 1024 | #15
| Device | Voltage | ADC steps | Notes |
|:-------------|:-------:|:---------:|:--------|
| Arduino UNO | 5.0V | 1024 | tested with RobotDyn ACS712 20 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.
Please let me know of other working platforms / processors.
## Interface
@ -53,17 +76,26 @@ Please let me know of other working platforms.
- **ACS712(uint8_t analogPin, float volts = 5.0, uint16_t maxADC = 1023, float mVperAmpere = 100)** constructor.
It defaults a 20 A type sensor, which is defined by the default value of mVperAmpere. See table below.
Volts is the voltage used by the (Arduino) internal ADC. maxADC is the maximum output of the internal ADC.
The defaults are based upon an Arduino UNO.
The defaults are based upon an Arduino UNO, 10 bits ADC.
These two ADC parameters are needed to calculate the voltage output of the ACS712 sensor.
- **int mA_AC(float frequency = 50)** blocks ~21 ms (depending on the frequency) to sample a whole 50 or 60 Hz period.
Since version 0.2.2 frequencies other integer values than 50 and 60 are supported, the lower the frequency,
the longer the blocking period.
Since version 0.2.3 floating point frequencies are supported to tune even better.
- **int mA_DC()** blocks < 1 ms (Arduino UNO) as it calls **analogRead()** twice.
A negative value indicates the current flows in the other direction.
- **int mA_AC(float frequency = 50, uint16_t cycles = 1)** blocks ~21 ms to sample a whole 50 or 60 Hz period.
Note that a lower frequency, or more cycles, will increase the blocking period.
The function returns the AC current in mA.
Its working is based upon multiplying the peak2peak value by the FormFactor.
- 0.2.2 frequencies other integer values than 50 and 60 are supported.
- 0.2.3 floating point frequencies are supported to tune even better.
- 0.2.8 the parameter cycles allow to average over a number of cycles.
- **float mA_AC_sampling(float frequency = 50, uint16_t cycles = 1)** blocks ~21 ms to sample a whole period.
The function returns the AC current in mA. (Note it returns a float).
Its working is based upon sampling a full period and take the square root of the average sumSquared.
This function is intended for signals with unknown Form Factor.
- 0.2.8 the parameter cycles allow to average over a number of cycles.
- **int mA_DC(uint16_t cycles = 1)** blocks < 1 ms (Arduino UNO) as it calls **analogRead()** twice.
A negative value indicates the current flows in the opposite direction.
- 0.2.8 the parameter cycles allow to average over a number of cycles.
#### Resolution
#### Resolution ACS712
| Sensor | mVperA | LSB 10bit | LSB 12bit | LSB 16bit |
|:---------|:--------:|:-----------:|:-----------:|:-----------:|
@ -75,17 +107,21 @@ A negative value indicates the current flows in the other direction.
#### Midpoint
- **void setMidPoint(uint16_t midPoint)** sets midpoint for the ADC conversion.
- **void autoMidPoint(float frequency = 50)** Auto midPoint, assuming zero DC current or any AC current.
Note it will block for 2 periods. Since version 0.2.2 frequencies other than 50 and 60 are supported.
By setting the frequency to e.g 1, the code will sample for 2 seconds, possibly getting a better average.
- **void autoMidPoint(float frequency = 50, uint16_t cycles = 1)** Auto midPoint,
assuming zero DC current or any AC current.
Note the function blocks for at least 2 periods.
By increase the number of cycles it averages multiple measurements, possibly getting a better midPoint.
This function is mandatory for measuring AC.
- 0.2.2 frequencies other than 50 and 60 are supported.
- 0.2.8 the parameter cycles allow to average over a number of cycles.
- **uint16_t getMidPoint()** read the value set / determined.
- **void incMidPoint()** manual increase midpoint, e.g. useful to manually adjust the midPoint in an interactive application.
- **void incMidPoint()** manual increase midpoint, e.g. useful in an interactive application.
- **void decMidPoint()** manual decrease midpoint.
#### Form factor
Also known as crest factor; affects AC signals only.
Also known as crest factor. Only used for signals measured with **mA_AC()**.
- **void setFormFactor(float formFactor = ACS712_FF_SINUS)** manually sets form factor.
Must typical be between 0.0 and 1.0, see constants below.
@ -112,7 +148,7 @@ Please let me know if other crest factors need to be added.
Default = 21 mV.
- **void setNoisemV(uint8_t noisemV = 21)** set noise level,
is used to determine zero level e.g. in AC measurements.
is used to determine zero level e.g. in the AC measurements with **mA_AC()**.
- **uint8_t getNoisemV()** returns the set value.
@ -162,7 +198,7 @@ ACS712 ----[ R1 ]----o----[ R2 ]---- GND
```
The voltage divider gave an error of about a factor 2 as all voltages were divided,
including the "offset" from the zero current level.
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".
@ -180,7 +216,7 @@ After using a voltage divider one need to adjust the mVperAmp.
| 10200 | 9800 | 9800 / (10200 + 9800) = 0.4900 | 100 \* 0.4900 = 49.00 |
**Note:** setting the midPoint correctly is needed when using a voltage divider.
**Note:** setting the midPoint correctly is also needed when using a voltage divider.
## Operation
@ -204,28 +240,55 @@ The examples show the basic working of the functions.
## Future
#### Should
#### Should - 0.3.x
- return types (0.4.0)
- fix Form Factor in **mA_AC()**
- the value set in **setFormFactor()** is not used!
- return types
- float for **mA_AC()** and **mA_DC()**
- actual value for **midPoint()** functions instead of void.
- midPoint need to be a float so it can be set more exact.
- **autoMidPoint()** should have cycles parameter too.
- investigate blocking calls:
- **mA_AC()** blocks for about 20 ms at 50 Hz.
This might affect task scheduling on a ESP32. Needs to be investigated.
Probably need a separate thread that wakes up when new analogRead is available?
- RTOS specific class?
- **detectFrequency(float)** blocks pretty long.
- investigate support for micro-Amperes. **ACS.uA_DC()**
- pre-calculate **\_AmperePerStep** to remove expensive float division.
- should mA per step be more efficient?
- more testing.
- add count parameter to **mA_DC(uint8_t count = 1)** to takes multiple
readings instead of just one.
When **mA_DC()** returns a float this could improve accuracy a (small) bit.
- investigate noise suppression
- update readme.md file
- should cycles be an uint8_t ?
#### Could
- do we need a **int point2point(float frequency)** function for AC.
Is technically a part of mA_AC() already.
Needs extra global variables, which are slower than local ones
Or just cache the last p2p value?
- do we need a **int peak2peak(float frequency)** function for AC.
- Is technically a part of mA_AC() already.
- Needs extra global variables, which are slower than local ones
- Or just cache the last p2p value?
- function **measure_AC()** + getters?
- ==> breaking interface ==> 0.4.0 ?.
- int **getMax()** idem
- int **getMin()** idem
- ACS712X class with external ADC ( 16 or even 24 bit)
- keep interface alike?
- external history file = changelog.md
- Should the FormFactor not be just a parameter of **mA_AC()**
it is the only function using it.
#### Won't
- external analogue read support? separate class!
- after this one stabilized.
- investigate support for micro-Amperes. **ACS.uA_DC()**
- need a very stable voltage
- needs a 24 bit ADC
- default noise is already ~21mV...
- => not feasible in normal setup.

View File

@ -45,6 +45,9 @@ unittest(test_constants)
assertEqualFloat(1.0, ACS712_FF_SQUARE, 0.001);
assertEqualFloat(1.0/sqrt(3), ACS712_FF_TRIANGLE, 0.001);
assertEqualFloat(1.0/sqrt(3), ACS712_FF_SAWTOOTH, 0.001);
assertEqual(21, ACS712_DEFAULT_NOISE);
assertEqual(50, ACS712_DEFAULT_FREQ);
}
@ -81,6 +84,17 @@ unittest(test_mA_AC)
}
unittest(test_mA_AC_samples)
{
ACS712 ACS(A0, 5.0, 1023, 100); // analogPin, volts, maxADC, mVperA
// loop with micros and a lot (150?) of analogReads - not possible
// assertEqual(0, ACS.mA_AC_samples(50));
// assertEqual(0, ACS.mA_AC_samples(60));
assertEqual(1, 1);
}
unittest(test_midPoint)
{
ACS712 ACS(A0, 5.0, 1023, 100); // analogPin, volts, maxADC, mVperA
@ -146,17 +160,34 @@ unittest(test_mVperAmp)
float mpa = ACS.getmVperAmp();
assertEqualFloat(100, mpa, 0.001); // default value..
float mv = 25.0;
while (mv < 200)
float mva = 25.0;
while (mva < 200)
{
ACS.setmVperAmp(mv);
ACS.setmVperAmp(mva);
mpa = ACS.getmVperAmp();
assertEqualFloat(mv, mpa, 0.001);
mv += 24.8; // just a bit random adjustments
assertEqualFloat(mva, mpa, 0.001);
mva += 24.8; // just a bit random adjustments
}
}
unittest(test_AmperePerStep)
{
ACS712 ACS(A0, 5.0, 1023, 100);
float aps = ACS.getAmperePerStep();
assertEqualFloat(0.047885, aps, 0.001);
float mva = 25.0;
while (mva < 200)
{
ACS.setmVperAmp(mva);
aps = ACS.getAmperePerStep();
assertEqualFloat(5000.0/1023/mva, aps, 0.001);
mva += 24;
}
}
unittest_main()