adc_zero = 512; //as read when there is no current on the load
sensitivity = 0.04; //as read from datasheet
for(i=0; i<samplesnum ; i++)
adc_raw = analogRead(1);
currentacc += (adc_raw - adc_zero) * (adc_raw - adc_zero); //subtract the zero-current value (512 or thereabouts) from the raw readings, square that, and add it to an accumulator variable
}
currentac = sqrt(currentacc)/samplesnum; //take the square root if the accumulator and divide by the number of readings.
for [u]DC current[/u] something like this
adc_zero = 512; //as read when there is no current on the load
sensitivity = 0.04; //as read from datasheet
currentdc = (adc_raw - adc_zero) * 5/(sensitivity * 1024)
right?
Almost. You need to convert (adc_raw - adc_zero) to type 'long' before you square it, because the square may exceed the range of an int. You will need to declare currentac with type 'long' too. After taking the sqrt, you need to adjust it for the sensitivity etc. just as you do for DC.
MarkT:
That's a confusing order, I'd apply conversions one-by-one:
Just remember that if you apply them one by one, you reduce the ability of the compiler to fold the constants, so there are more floating point operations to do at runtime. That may be easier for debugging, but is not so good for getting good performance.
dc42:
Almost. You need to convert (adc_raw - adc_zero) to type 'long' before you square it, because the square may exceed the range of an int. You will need to declare currentac with type 'long' too. After taking the sqrt, you need to adjust it for the sensitivity etc. just as you do for DC.
i've left all untyped i know
typed code is below.
i've tried two ways, but it doesn't seams to works.
test 1------------------
dc42:
subtract the zero-current value (512 or thereabouts) from the raw readings, square that, and add it to an accumulator variable. When you have accumulated enough samples, take the square root if the accumulator and divide by the number of readings.
double d = 0; long l = 0;
for(;;) {
uint16_t i = 0;
int16_t adc_raw = 0;
int16_t adc_zero = 512;
for(i=0; i<100; i++) {
adc_raw = adc_read(2);
l += pow(adc_raw - adc_zero, 2);
_delay_us(100);
}
d = (sqrt(l)/100) * 5 / (0.04 * 1024.0);
l = 0;
test 2------------------
dc42:
average the square of the current, and take the square root of the average
double c = 0, d = 0;
for(;;) {
uint16_t i = 0;
int16_t adc_raw = 0;
int16_t adc_zero = 512;
for(i=0; i<100; i++) {
adc_raw = adc_read(2);
adc_raw = adc_raw - adc_zero;
c += pow(adc_raw * 5 / (0.04 * 1024.0), 2);
_delay_us(100);
}
d = sqrt(c)/100;
c = 0;
... print c...
}
i'm testing a 6.4 Amp 220V 50hz device
both code i obtain almost the same value, which is 0.27Amp, but is not correct
You don't want to use pow() here, it uses float, double and is far too slow (it does logarithms, antilogarithms).
long val = adc_raw - adc_zero ;
l += val * val ;
You need to be sampling the waveform at 1000Hz or more for good accuracy so its important
the maths isn't too slow. Lose the delays - if you want to control the timing you should use millis() or
micros() so you can synchronize to a multiple of 50 or 60Hz.
First look at a set of raw values from the sensor to check they are what you'd expect (sample a
bunch into an array, then print out the array, printing out is too slow also). You should see a
clear sinusoidal pattern with the correct amplitude.
Then test your calculation code with known correct raw values to see if the arithmetic is correct.
You don't want to use pow() here, it uses float, double and is far too slow (it does logarithms, antilogarithms).
long val = adc_raw - adc_zero ;
l += val * val ;
You need to be sampling the waveform at 1000Hz or more for good accuracy so its important
the maths isn't too slow. Lose the delays - if you want to control the timing you should use millis() or
micros() so you can synchronize to a multiple of 50 or 60Hz.
First look at a set of raw values from the sensor to check they are what you'd expect (sample a
bunch into an array, then print out the array, printing out is too slow also). You should see a
clear sinusoidal pattern with the correct amplitude.
Then test your calculation code with known correct raw values to see if the arithmetic is correct.
attached a plot of raw values, just analogRead of the pin.
it has a strange behavior
the first part (y=512) is a line, device is power off.
the second part, some sort of 1/2 sine wave, only + part is present, device @ 3.4Amp, [strange here, why??]
the third part is a sine wave, device @ 6.4Amp
sensor has the same circuit as the datasheet propose, it does not change if Cf and / or Rf are present or not.
dc42:
What is the device whose current draw you are measuring?
I would ask that also. Current is only drawn in sinewave form if the load is a pure resistance, many loads just 'sip' current when the voltage raises above a certain value. Do an analysis of what AC current input would look like if wired to a typical full wave capacitance filtered DC power supply. The supply only draws current if the DC load wired to the power supply 'pulls' down the voltage stored in the filter capacitors below the peak DC value the caps can charge to. Expecting pure sine wave current flow with real world loads will cause much errors in your 'measurements'. You will need a 'true RMS' calculation derived from the raw sampled measurement values.
it's a 1500w hair dryer with three position (stop, 3.4A, 6.4A, as read by my multimeter, which is not true rms).
i've also try attaching another heater, from 6.4A the current sucked it sine wave form BUT, and the extimated current measure is right IF i change the sensitivity A LOT.
uint16_t i = 0;
double sensitivity = 0.0016;
int16_t adc_raw = 0;
int16_t adc_zero = 512; //as read when there is no current on the load
for(i=0; i<100; i++) {
adc_raw = adc_read(2);
l += (adc_raw - adc_zero)*(adc_raw - adc_zero);
_delay_us(100);
}
d = (sqrt(l)/100) * 5 / (0.0016 * 1024.0) - 0.5;
l = 0;
d = (sqrt(l)/100) * 5 / (0.0016 * 1024.0) - 0.5;
with sensitivity of 0.0016 and an offset of 0.5 i've almost right result on 0A and above 3.4A (tried only 6.4A / 8A / 12A)
but from datasheet sensitivity is 0.04 ( = 25 * 0.0016)
So, if my calculations are correct and your meter is to be believed, the sensitivity of your device is only about half what it should be. Are you sure you have the 50A version and not the 100A version of the ACS758?
I would tidy up the code like this:
const double sensitivity = 0.04;
const int16_t adc_zero = 512; //as read when there is no current on the load
const uint16_t numSamples = 100;
float current;
...
long acc = 0;
for(uint16_t i=0; i<numSamples ; i++) {
int16_t adc_raw = adc_read(2);
acc += (adc_raw - adc_zero)*(adc_raw - adc_zero);
_delay_us(100);
}
const float meanSquare = ((float)acc)/((float)numSamples);
current = sqrt(meanSquare) * (5 / (0.0016 * 1024.0)) - 0.5;
While writing this, I noticed that you were dividing by the number of samples after taking the square root instead of before. That explains a factor of 10 in why the sensitivity appeared to be incorrect.
i change the ratiometric change in sesitivity derived from my voltage (page 16 acs758 datasheet)
SENSnew = (Vin / 5v) SENSdatasheet
my board supply is 4.70V so the sensitivity should be 37.6mV, next 0.036 / 2 = 0.018.. ?
The extra factor of 2 does seem odd, that's why I suspected that you might have the 100A version of the sensor. Did you get it from a reputable supplier?
It might be a remarked chip, perhaps a -100B that has been remarked as a -050B by mistake. How well do the markings match the ones shown on the last 2 pages of the datasheet?
i've made further test
it seems that with a sensitivity of 0.034/2 it works from 4A to 12A quite well.
0.034 seems ok, because my supply is 4.7V, sensitivity should be 0.03824 +- 5mV (Electrical offset voltage from datasheet)
but i've to use half of it.
p.s checked also my multimeter and it has no problem.
have you any suggestion, or anyone here that can test my code on other chip?