Skip to content

Commit

Permalink
change mVperAmp to float + refactor (#17)
Browse files Browse the repository at this point in the history
* change mVperAmpere to float + refactor
* add **ACS712_FF_SAWTOOTH**  form factor / crest factor
* update documentation / naming parameters / minor edits
  • Loading branch information
RobTillaart authored Aug 12, 2022
1 parent 1f7c0d3 commit 1dc910c
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 149 deletions.
56 changes: 32 additions & 24 deletions ACS712.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// FILE: ACS712.cpp
// AUTHOR: Rob Tillaart, Pete Thompson
// VERSION: 0.2.6
// VERSION: 0.2.7
// DATE: 2020-08-02
// PURPOSE: ACS712 library - current measurement
//
Expand All @@ -19,62 +19,69 @@
// 0.2.4 2021-11-22 add experimental detectFrequency()
// 0.2.5 2021-12-03 add timeout to detectFrequency()
// 0.2.6 2021-12-09 update readme.md + license
// 0.2.7 2022-08-10 change mVperAmp to float
// add ACS712_FF_SAWTOOTH
// update readme.md + unit test + minor edits


#include "ACS712.h"


ACS712::ACS712(uint8_t analogPin, float volts, uint16_t maxADC, uint8_t mVperA)
ACS712::ACS712(uint8_t analogPin, float volts, uint16_t maxADC, float mVperAmpere)
{
_pin = analogPin;
_mVpstep = 1000.0 * volts / maxADC; // 1x 1000 for V -> mV
_mVperAmpere = mVperA;
_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
_noisemV = 21; // Noise is 21mV according to datasheet
}


int ACS712::mA_AC(float freq)
int ACS712::mA_AC(float frequency)
{
uint16_t period = round(1000000UL / freq);
uint16_t period = round(1000000UL / frequency);
uint16_t samples = 0;
uint16_t zeros = 0;

int _min, _max;
_min = _max = analogRead(_pin);

// remove expensive float operation from loop.
uint16_t zeroLevel = round(_noisemV/_mVpstep);
uint16_t zeroLevel = round(_noisemV/_mVperStep);

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 point2point = (_max - _min);

// automatic determine _formFactor / crest factor
// automatic determine _formFactor / crest factor
float D = 0;
float FF = 0;
if (zeros > samples * 0.025) // more than 2% zero's
// 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
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
else // # zeros is small => D --> 1 --> sqrt(D) --> 1
{
FF = ACS712_FF_SINUS;
}
_formFactor = FF;

// value could be partially precalculated: C = 1000.0 * 0.5 * _mVpstep / _mVperAmpere;
// return 1000.0 * 0.5 * point2point * _mVpstep * _formFactor / _mVperAmpere);
return round( (500.0 * point2point) * _mVpstep * _formFactor / _mVperAmpere);
// 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);
}


Expand All @@ -83,16 +90,17 @@ int ACS712::mA_DC()
// read twice to stabilize the ADC
analogRead(_pin);
int steps = analogRead(_pin) - _midPoint;
return 1000.0 * steps * _mVpstep / _mVperAmpere;
float mA = 1000.0 * steps * _mVperStep / _mVperAmpere;
return round(mA);
}


// 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 freq)
void ACS712::autoMidPoint(float frequency)
{
uint16_t twoPeriods = round(2000000UL / freq);
uint16_t twoPeriods = round(2000000UL / frequency);

uint32_t total = 0;
uint32_t samples = 0;
Expand All @@ -101,8 +109,8 @@ void ACS712::autoMidPoint(float freq)
{
uint16_t reading = analogRead(_pin);
total += reading;
samples ++;
// Delaying ensures we won't overflow since we'll perform a maximum of 40,000 reads
samples++;
// Delaying ensures we won't overflow since we'll perform a maximum of 40,000 reads
delayMicroseconds(1);
}
_midPoint = total / samples;
Expand Down Expand Up @@ -133,9 +141,9 @@ float ACS712::detectFrequency(float minimalFrequency)
int Q1 = (3 * minimum + maximum ) / 4;
int Q3 = (minimum + 3 * maximum ) / 4;

// 10x passing Quantile points
// wait for the right moment to start
// to prevent endless loop a timeout is checked.
// 10x passing Quantile points
// wait for the right moment to start
// to prevent endless loop a timeout is checked.
timeOut *= 10;
start = micros();
while ((analogRead(_pin) > Q1) && ((micros() - start) < timeOut));
Expand Down
81 changes: 42 additions & 39 deletions ACS712.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// FILE: ACS712.h
// AUTHOR: Rob Tillaart, Pete Thompson
// VERSION: 0.2.6
// VERSION: 0.2.7
// DATE: 2020-08-02
// PURPOSE: ACS712 library - current measurement
//
Expand All @@ -12,14 +12,15 @@

#include "Arduino.h"

#define ACS712_LIB_VERSION (F("0.2.6"))
#define ACS712_LIB_VERSION (F("0.2.7"))


// ACS712_FF_SINUS == 1.0/sqrt(2) == 0.5 * sqrt(2)
// should be smaller in practice 0.5 ?
#define ACS712_FF_SINUS (1.0/sqrt(2))
#define ACS712_FF_SQUARE (1.0)
#define ACS712_FF_TRIANGLE (1.0/sqrt(3))
#define ACS712_FF_SAWTOOTH (1.0/sqrt(3))


class ACS712
Expand All @@ -30,64 +31,66 @@ class ACS712
// (1) the milliVolt per Ampere and
// (2) the volts parameter.
//
// TYPE mV per Ampere
// 5A 185
// 20A 100
// 30A 66
ACS712(uint8_t analogPin, float volts = 5.0, uint16_t maxADC = 1023, uint8_t mVperA = 100);
// TYPE mV per Ampere
// 5A 185.0
// 20A 100.0
// 30A 66.0
ACS712(uint8_t analogPin, float volts = 5.0, uint16_t maxADC = 1023, float mVperAmpere = 100);


// returns mA
// blocks 20-21 ms to sample a whole 50 or 60 Hz period.
// lower frequencies block longer.
int mA_AC(float freq = 50);
// returns mA
// blocks 20-21 ms to sample a whole 50 or 60 Hz period.
// lower frequencies block longer.
int mA_AC(float frequency = 50);


// returns mA
// blocks < 1 ms
int mA_DC();
// returns mA
// blocks < 1 ms
int mA_DC();


// midpoint ADC for DC only
inline void setMidPoint(uint16_t mp) { _midPoint = mp; };
inline uint16_t getMidPoint() { return _midPoint; };
inline void incMidPoint() { _midPoint++; };
inline void decMidPoint() { _midPoint--; };
// Auto midPoint, assuming zero DC current or any AC current
void autoMidPoint(float freq = 50);
// midPoint ADC for DC only
void setMidPoint(uint16_t midPoint) { _midPoint = midPoint; };
uint16_t getMidPoint() { return _midPoint; };
void incMidPoint() { _midPoint++; };
void decMidPoint() { _midPoint--; };
// Auto midPoint, assuming zero DC current or any AC current
void autoMidPoint(float frequency = 50);


// also known as crest factor; affects mA_AC() only
// default sinus.
inline void setFormFactor(float ff = ACS712_FF_SINUS) { _formFactor = ff; };
inline float getFormFactor() { return _formFactor; };
// 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; };


// noise defaults 21
inline void setNoisemV(uint8_t noisemV = 21) { _noisemV = noisemV; };
inline uint8_t getNoisemV() { return _noisemV; };
void setNoisemV(uint8_t noisemV = 21) { _noisemV = noisemV; };
uint8_t getNoisemV() { return _noisemV; };


// AC and DC
inline void setmVperAmp(uint8_t mva) { _mVperAmpere = mva; };
inline uint8_t getmVperAmp() { return _mVperAmpere; };
// AC and DC
void setmVperAmp(float mVperAmpere) { _mVperAmpere = mVperAmpere; };
float getmVperAmp() { return _mVperAmpere; };


// Experimental 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; };
// 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; };


private:
uint8_t _pin;
float _mVpstep; // millivolt per step
float _formFactor; // point2point -> RMS
uint8_t _mVperAmpere;
float _mVperStep;
float _formFactor; // point2point -> RMS
float _mVperAmpere;
uint16_t _midPoint;
uint8_t _noisemV;
float _microsAdjust = 1.0; // 0.9986
float _microsAdjust = 1.0; // 0.9986
};


// -- END OF FILE --

Loading

0 comments on commit 1dc910c

Please sign in to comment.