Problem with multiple readings

Hello

I'm using this sketch to measure the analogread from A0 and take multiple readings.
It works so far with 16 samples but when I change it to 60 or 80 I get negative results (16 is the original value set to nSamp).
This is the page where I got it from.

/*
 * measure adc voltage accurately by taking multiple readings - see notes on this page
 * http://www.skillbank.co.uk/arduino/adc1.htm
 * 
 * www.skillbank.co.uk Sept 2020
 */
//Analog input and pin assignments
const byte V1Pin = A0;       // use will use analog pin 1 for voltage 1
int reading; //the value we read from the ADC

// sampling parameters
int nSamp = 80;   // Number of Samples to take for each reading - best if it's a power of two.
int interval = 7; // milliseconds interval between successive readings
unsigned long sum1 = 0;  // the total of nSamp readings from the ADC

//calculating voltages
int result; // calculated value of measured voltage in millivolts
//float vScale=5;  //choose the right scale to suit the reference you have chosen.
//float vScale=3.3;  
//float vScale=2.56;
float vScale=1.1;
//*** If you calibrate the system you can adjust the value of vScale to give more accurate readings.

void setup()
{
  Serial.begin(115200);

  analogReference(INTERNAL);    // program is being tested on a 5V Arduino Micro Pro - 32U4 chip so this will be 5.0V
  //*** The chip on your Arduino - depending on type -  is provided with SOME of the following reference voltages 
  //DEFAULT: the default analog reference of 5 volts (on 5V Arduino boards) or 3.3 volts (on 3.3V Arduino boards)
  //INTERNAL: a built-in reference, equal to 1.1 volts on the ATmega168 or ATmega328 and 2.56 volts on the ATmega8 and 32U4 chip boards.
  //EXTERNAL: the voltage applied to the AREF pin.  The Arduino Micro Pro does not have a pin to let you do this

  vScale = vScale * 1000/1024; //scale the reading to read in millivolts
  
  reading = analogRead(V1Pin);     // dummy read to settle ADC Mux
  delay(5);
}

void loop()
{
  sum1 = 0; //ready to start adding values
  //add up nSamp successive readings; 
  for (byte count = 0; count < nSamp; count++ )
  {
    reading = analogRead(V1Pin);     // actual read
    sum1 = sum1 + reading;
    delay(interval);
  }
  // we print reading values to help with calibration
  //Serial.print("Total of ");
  //Serial.print(nSamp);
  //Serial.print(" readings is: ");
  Serial.print(String(sum1));

  result = convReadings(sum1, nSamp, vScale);
  Serial.print(" ");
  //Serial.print("Voltage is  ");
  Serial.println(result);
  //Serial.println(" millivolts.");
  delay(10);
}

//
// Calculate average sensor reading, convert to voltage
//
int convReadings(int sum, int number, float scale)
{
  sum = sum + (number >>1);  // add a correction of 0.5 of the reading - see notes
  float mV = sum * scale;  // mV = sum * (Vref  * 1000 / 1024 ) 
  mV = mV / number; //divide by number of readings last to keep precision high
  return ((int) mV); //return the result as an integer as decimal point values arent valid
  // (int) is a "cast" to change a float variable to an integer. Values after the decimal point are "cut off" so float 1.93 becomes int 1
}


Maybe I miss something or there is a reason why it's 16 (and obiously I don'i get it).
Any help is appreciated.

30 samples would work.
33 samples not so well.

1 Like

Hint
What's 32767 / 1023?

32,03030303030303

I get the 1023 but where do you get the 32767 ?

you must think in terms of data size value:

1023 is the max value for a 10bit number and 32767 is quite the half way to 65535, wich is the max value for a 16bit unsigned number (r max value for a signed number).

You declared your data "readings" as a int, so I can assume it is a 16bit (it actually depends on your board).
by doing successive sum of the reading data, I guess you reach the limit of a 16bit number. Therefore, your calculation is not true anymore.

Plan should be:

  • always take care of the format data;
  • do mean calculation with fewer values, or with smaller values, or in several steps

2nd hint. While your sum1 is a long, what type is the incoming variable "sum", in the subroutine convReadings()? And what is the maximum positive value it can hold?

Thanks for your help and hints.

I have now in the setup the same routine as in the loop (with "sum" as int) but interestingly it did not produce negative results even when I read 100 samples.

for (byte count = 0; count < 100; count++) {
    reading = analogRead(V1Pin);  // actual read
    sum1 = sum1 + reading;
    delay(7);
  }
  ref = convReadings(sum1, 100, vScale);

Hi @donsenilo1968,

if you replace

  reading = analogRead(V1Pin);  // actual read

by

 reading = 1023;  // simulate maximum

does it still not produce negative values?

The general problem with convReadings() stays the same whether you use it in setup() or in loop() or elsewhere.

As sum1 is an unsigned long, why don't you just change the parameter declaration in

int convReadings(int sum, int number, float scale)

to

int convReadings(unsigned long sum, int number, float scale)

That would avoid an overflow and is the clean way to handle the interface ...

Regards
ec2021

Feel free to try this:

float vScale=1.1;

void CheckConvertWith(unsigned long value){
  Serial.print(value);
  Serial.print("\t");
  Serial.println(convReadings(value,100,vScale));
  Serial.println("-------------------------------------------");
}

void setup()
{
  Serial.begin(115200);
  vScale = vScale * 1000/1024; //scale the reading to read in millivolts
  Serial.println("long\tsum\tsum+50\tresult");
  for (unsigned long s = 98250;s < 98260;s++) CheckConvertWith(s);

  for (unsigned long s = 65530;s < 65540;s++) {
    Serial.print(s);
    Serial.print("\t");
    Serial.println(int (s));
  };

  while(1);
} 

void loop() {
}

//
// Calculate average sensor reading, convert to voltage
//
int convReadings(int sum, int number, float scale)
{
  Serial.print(sum);
  Serial.print("\t");
  sum = sum + (number >>1);  // add a correction of 0.5 of the reading - see notes
  Serial.print(sum);
  Serial.print("\t");
  float mV = sum * scale;  // mV = sum * (Vref  * 1000 / 1024 ) 
  mV = mV / number; //divide by number of readings last to keep precision high
  return ((int) mV); //return the result as an integer as decimal point values arent valid
  // (int) is a "cast" to change a float variable to an integer. Values after the decimal point are "cut off" so float 1.93 becomes int 1
}

It tests the function convReadings() as you use it now. The results for a certain range are:


long	sum	sum+50	result
98250	32714	32764	351
-------------------------------------------
98251	32715	32765	351
-------------------------------------------
98252	32716	32766	351
-------------------------------------------
98253	32717	32767	351
-------------------------------------------
98254	32718	32768	-352
-------------------------------------------
98255	32719	32769	-351
-------------------------------------------
98256	32720	32770	-351
-------------------------------------------
98257	32721	32771	-351
-------------------------------------------
98258	32722	32772	-351
-------------------------------------------
98259	32723	32773	-351
-------------------------------------------
  • Column "long" is the input value
  • Column "sum" the value of sum inside convReadings(); that's what is left over from the "long" value
  • Column "sum+50" is the value of sum after (number >>1) has been added (which adds 50 with number = 100)
  • Column "result" shows the return value of the function

At the end of Serial output you will see the following:

65530	-6
65531	-5
65532	-4
65533	-3
65534	-2
65535	-1
65536	0
65537	1
65538	2
65539	3

That shows you what a cast from unsigned long to int does around 65535 ...

If you change the for-range to go from 32766 to 32770 you will find another change there (as already mentioned by others here).

Regards
ec2021

1 Like

I've changed and testet it and it works.

Thanks again for your support !

1 Like

About 80% of programming is debugging ... :wink: Breaking up sketches into relevant (short) pieces is crucial for debugging purposes.

Good luck and have fun!
ec2021

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.