Adafruit_ADS1X15 icon indicating copy to clipboard operation
Adafruit_ADS1X15 copied to clipboard

Problem with computeVolt and compiler optimisation

Open anvgfr opened this issue 6 months ago • 1 comments

Hello,

using your library to make data acquisition i noticed that sometimes computeVolt return a bad result, due to some compiler optimisation, like in this output :

Channel 1 and 2 are wrong : rms is rounded as int before calling computeVolt to get Vapp, it should be 0V !

# Channel 0 : rms=0.8519 / min=0 / max=2 /  Vapp = 97897.4375V / min=0.0000V / max=0.0040V /  I=5933178.0000A
# Channel 1 : rms=0.9333 / min=-1 / max=3 /  Vapp = 0.0000V / min=-0.0020V / max=0.0060V /  I=0.0000A
# Channel 2 : rms=0.9070 / min=0 / max=2 /  Vapp = 68943856272903503872.0000V / min=0.0000V / max=0.0040V /  I=4178415527426349072384.0000A
# Channel 3 : rms=0.7184 / min=-1 / max=1 /  Vapp = -0.0000V / min=-0.0020V / max=0.0020V /  I=-0.0000A
# Channel 4 : rms=0.9837 / min=-1 / max=3 /  Vapp = -0.0000V / min=-0.0020V / max=0.0060V /  I=-0.0020A

I am running on ESP32 with Arduino_ESP32 library (Arduino IDE version 2.3.2), AdaFruit latest release (2.5.0). Chips is an ADS1015 (from techniot bought on Amazon)

Original parts of the code producing random wrong value some times:

> > MainPowerChannel.RMSValue = MainPowerChannel.RMSValue / MainSampleRead;
> > MainPowerChannel.RMSValue = sqrt(MainPowerChannel.RMSValue);
> > // Convert to Volt using ADC resolution
> > MainPowerChannel.Vapp = adcComputeVolts(iMainVoltageADC, MainPowerChannel.RMSValue);
> 

(source is used to get the right ADC object to used on a list of 2...)

float adcComputeVolts(int _source, int16_t value) {
  if (_source >= NB_MAX_ADC_CHANNEL) return -1;
  int adsid = _source / NB_CHANNEL_BY_ADC;
  int adspin = _source % NB_CHANNEL_BY_ADC;
  //Serial.printf("ACV? %d=(%d,%d) %d\r\n",_source,adsid,adspin,value);
  //int16_t retvalue=value*1;
  return ads[adsid].computeVolts(value);
}

to solve temporarly this problem i had to use the source value before calling computeVolt in my own code (using a printf or another operation. (removing one or the other comment)

reading the current library code, i found that the current operation give priority on the division of the full range value (float) by the resolution BEFORE applying current source value.

float Adafruit_ADS1015::computeVolts(int16_t counts) 
{
  // see data sheet Table 3
  float fsRange;
  switch (m_gain) {
  case GAIN_TWOTHIRDS:
    fsRange = 6.144f;
    break;
  case GAIN_ONE:
    fsRange = 4.096f;
    break;
  case GAIN_TWO:
    fsRange = 2.048f;
    break;
  case GAIN_FOUR:
    fsRange = 1.024f;
    break;
  case GAIN_EIGHT:
    fsRange = 0.512f;
    break;
  case GAIN_SIXTEEN:
    fsRange = 0.256f;
    break;
  default:
    fsRange = 0.0f;
 }
 return counts * (fsRange / (32768 >> m_bitShift));
}

So i changed the order of the code to do the multiplication first then the division (better for accurancy)

  // Change order of operation to do upscaling before downscaling;
  return (fsRange*counts) / (32768 >> m_bitShift);

And now i did'nt get any problem with the calculation.

anvgfr avatar Aug 25 '24 11:08 anvgfr