Go Down

Topic: ac current meter using acs758 hall sensor (Read 7 times) previous topic - next topic

MarkT

Code: [Select]

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: [Select]

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.

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

dc42


so,
for AC current something like this
Code: [Select]
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: [Select]
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.
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.

hozone


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

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: [Select]
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: [Select]
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 :(

MarkT

Code: [Select]
    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: [Select]
    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.




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

hozone


Code: [Select]
    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: [Select]
    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.

Go Up