Pages: [1] 2   Go Down
Author Topic: ac current meter using acs758 hall sensor  (Read 6841 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 1
Posts: 35
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hello,

i'm trying to read ac current using a bidirection allegro hall sensor acs758-050b
i've a problem using it.
when i do not connect anything to this sensor i read
* a raw value of 516 with a input voltage of 4.80V +-0.05V
* a raw value of 512 with a input voltage of 4.70V +-0.05V

to convert the value to current i use the current formula
adcvref = 5
adcdiv = 1023
sensitivity = 0.04 ( acs758-050b sensitivity datasheet)
amp = (adcraw*adcvref/adcdiv- adcvref/2) / sensitivity;

anyway the problem with the raw value persist, even if i change the reference (adcvref) current value to real adcvalue (4.80 or 4.70)

can anyone help me?
thanks!
Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 227
Posts: 6639
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The value adcdiv in your calculation should be 1024, not 1023. A value of 512 with no current flowing is exactly right, however the device may have an offset of a few mV, so the value may be a few counts out. Noise on the +5V line will affect the reading.
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

0
Offline Offline
Newbie
*
Karma: 1
Posts: 35
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thank you for reply

The value adcdiv in your calculation should be 1024, not 1023
reason for 1024, correct me if i'm wrong
adcraw is a 10bit values, so from 0(min) to 1023(max = 2^10-1), so the max values here is 1023

A value of 512 with no current flowing is exactly right, however the device may have an offset of a few mV, so the value may be a few counts out. Noise on the +5V line will affect the reading.
which is the math to use for transform adc values to amepere using an acs758.

i get adc samples for adc current making an avarage of "many" values, i.e. at least 2 times the period of my 50Hz primary current, and my adc values are stable.
but maybe my formula is not correct, i'm using this one:
amp = (adcraw*adcvref/adcdiv- adcvref/2) / sensitivity;

because with "small" adcraw changes i have "big" change in amp
es.
(  512  *5/1023 - 5/2)/0.04 = 0.062   
(  516  *5/1023 - 5/2)/0.04 = 0.54    (1/2Amp!)
and also consider the max raw value of 1023
(  1023  *5/1023 - 5/2)/0.04 = 62.5
acs758-050b is rated as 50Amp max, the max raw value i should read is almost 900
Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 227
Posts: 6639
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

reason for 1024, correct me if i'm wrong
adcraw is a 10bit values, so from 0(min) to 1023(max = 2^10-1), so the max values here is 1023

The max value you can read is indeed 1023, but the correct value to use in the calulation is 1024. See section 24.7 of the atmega328p datasheet.

i get adc samples for adc current making an avarage of "many" values, i.e. at least 2 times the period of my 50Hz primary current, and my adc values are stable.

Are you trying to measure alternating current? If so, you need to average the square of the current, and take the square root of the average (that's what RMS means). Easiest way is to 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.

thank you for reply

but maybe my formula is not correct, i'm using this one:
amp = (adcraw*adcvref/adcdiv- adcvref/2) / sensitivity;

The device is ratiometric, so its sensitivity varies with Vcc. Use:

  current = (adc_raw - adc_zero) * 5/(sensitivity@5V * 1024)

(where adc_zero is the ADC reading you get when the current is zero, which will be close to 512), which is almost the same.

because with "small" adcraw changes i have "big" change in amp
es.
(  512  *5/1023 - 5/2)/0.04 = 0.062   
(  516  *5/1023 - 5/2)/0.04 = 0.54    (1/2Amp!)
and also consider the max raw value of 1023
(  1023  *5/1023 - 5/2)/0.04 = 62.5
acs758-050b is rated as 50Amp max, the max raw value i should read is almost 900

That's the nature of the device. If you put more than 50A through it then the accuracy will degrade.
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

0
Offline Offline
Newbie
*
Karma: 1
Posts: 35
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The max value you can read is indeed 1023, but the correct value to use in the calulation is 1024. See section 24.7 of the atmega328p datasheet.
Thank you for this info!

Are you trying to measure alternating current? If so, you need to average the square of the current, and take the square root of the average (that's what RMS means). Easiest way is to 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.
Yes ac current.

so,
for AC current something like this
Code:
samplesnum = 100;
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 DC current something like this
Code:
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?
Logged

0
Offline Offline
Shannon Member
****
Karma: 222
Posts: 12725
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
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)

That's a confusing order, I'd apply conversions one-by-one:
Code:
adc_zero = 512; //as read when there is no current on the load
sensitivity = 0.04; //as read from datasheet
amps_per_volt = 1.0 / sensitivity ;
fullscale_volts = 5.0 ;
fullscale_count = 1024.0 ;
ampsdc = ((adc_raw - adc_zero) / fullscale_count) * fullscale_volts * amps_per_volt ;

I'd always choose a name with explicit unit if possible "current" might be amps, milliamps or microamps, whereas "amps" is unambiguous.

Logged

[ I won't respond to messages, use the forum please ]

United Kingdom
Offline Offline
Tesla Member
***
Karma: 227
Posts: 6639
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

so,
for AC current something like this
Code:
samplesnum = 100;
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 DC current something like this
Code:
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.

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.
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

0
Offline Offline
Newbie
*
Karma: 1
Posts: 35
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 smiley-wink
typed code is below.


i've tried two ways, but it doesn't seams to works.

test 1------------------
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.

Code:
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------------------
average the square of the current, and take the square root of the average

Code:
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 smiley-sad
Logged

0
Offline Offline
Shannon Member
****
Karma: 222
Posts: 12725
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
    l += pow(adc_raw - adc_zero, 2);
You don't want to use pow() here, it uses float, double and is far too slow (it does logarithms, antilogarithms).

Code:
    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.




Logged

[ I won't respond to messages, use the forum please ]

0
Offline Offline
Newbie
*
Karma: 1
Posts: 35
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
    l += pow(adc_raw - adc_zero, 2);
You don't want to use pow() here, it uses float, double and is far too slow (it does logarithms, antilogarithms).

Code:
    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.


* acs758test.jpg (11.06 KB, 580x122 - viewed 60 times.)
Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 227
Posts: 6639
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

What is the device whose current draw you are measuring?
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 362
Posts: 17307
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Lefty
Logged

0
Offline Offline
Shannon Member
****
Karma: 222
Posts: 12725
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Its either using diode or thyristor to switch to half load?

Either way the RMS calculation should get the right answer.
« Last Edit: November 19, 2012, 09:25:38 pm by MarkT » Logged

[ I won't respond to messages, use the forum please ]

0
Offline Offline
Newbie
*
Karma: 1
Posts: 35
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Code:
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;

Code:
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)
Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 227
Posts: 6639
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

1. In the plot you provided in reply #9, at the 6.4A setting, what are the ADC readings at the high and low peaks?

2. How do you know that the current is 6.4A?
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Pages: [1] 2   Go Up
Jump to: