Problem reading two sensors with analogRead()

by Len17 on Sat Jun 20, 2009 4:11 pm

I've been having trouble using analogRead() to read values from two separate sensors. If I run a sketch that calls analogRead() for either one of the sensor inputs I get good results, but if my sketch has two analogRead() calls for both sensors, the values returned are inconsistent and incorrect.

Is there a problem with calling analogRead() for two different input pins in the same sketch?

Details:

I connected two sensors to separate analog input pins. The sensors are a CdS photocell and an LM35 temperature sensor. They're hooked up as described in http://www.ladyada.net/learn/sensors/.

I wrote a simple sketch to read the sensors and print the results out the serial port. The two analogRead() calls seem to interfere with each other. The temperature value is obviously incorrect, and varies wildly as I change the light level sensed by the photocell. But if I simply remove the analogRead() call for the photocell, the temp sensor value is perfectly steady and correct.

Here's the test sketch:
Code: Select all
void setup()
{
  Serial.begin(57600);
}

void loop()
{
  int nLight = 0;
  long nTemp = 0;
  nLight = analogRead(0); // remove this line to make the temp sensor work right!
  nTemp = analogRead(5) * 5000L / 1024L  / 10;
  Serial.print("Light = ");
  Serial.print(nLight);
  Serial.print("  Temp = ");
  Serial.println(nTemp);
  delay(250);
}


Here's the sketch's output. You can see the temp sensor change when I obscure the light sensor.
Code: Select all
Light = 598  Temp = 20
Light = 600  Temp = 20
Light = 601  Temp = 20
Light = 599  Temp = 20
Light = 532  Temp = 22
Light = 302  Temp = 27
Light = 265  Temp = 27
Light = 231  Temp = 27
Light = 223  Temp = 27


Here's the same test with the first analogRead() removed (so no light value). Now the temp value is constant and correct. (Yes, it's pretty warm in here today.)
Code: Select all
Light = 0  Temp = 27
Light = 0  Temp = 27
Light = 0  Temp = 27
Light = 0  Temp = 27
Light = 0  Temp = 27
Light = 0  Temp = 27
Light = 0  Temp = 27
Light = 0  Temp = 27
Light = 0  Temp = 27
User avatar
Len17
 
Posts: 393
Joined: Sat Mar 14, 2009 6:20 pm

Re: Problem reading two sensors with analogRead()

by karlgg on Sat Jun 20, 2009 11:15 pm

I assume your power supply is stable... Try putting a short delay after each read? See if that changes anything, giving a little "settling" time.
I think I am, therefore I am... I think.
User avatar
karlgg
 
Posts: 212
Joined: Sat Dec 27, 2008 1:41 pm
Location: Anthony, NM

Re: Problem reading two sensors with analogRead()

by fat16lib on Sun Jun 21, 2009 9:01 am

The problem is that the Atmega on the Arduino has one ADC that is multiplexed for all the analog pins.
When you do an analogRead(), a multiplexer connects the pin you are reading to the ADC. This works fine for low impedance voltage sources.

It takes time for a high impedance sensor like your temperature sensor to change the voltage at the ADC after this switch of pins. Temperature sensors must use low power and thus be high impedance to avoid IR heating.

Try the following:

Code: Select all
  analogRead(5);
  delay(10);
  nTemp = analogRead(5) * 5000L / 1024L  / 10;


The first analogRead(5) will switch the pin to the ADC. The delay will allow the voltage at the ADC to stabilize and the second analogRead(5) should get a stable value.
fat16lib
 
Posts: 591
Joined: Wed Dec 24, 2008 12:54 pm

Re: Problem reading two sensors with analogRead()

by Len17 on Sun Jun 21, 2009 1:52 pm

I did try adding some delays, but not with two calls to analogRead(5). I'll give that a try.

I'm sure the power is OK. I was testing with USB power, and I've run lots of other sketches with a few LEDs and whatnot, with no stability issues.
User avatar
Len17
 
Posts: 393
Joined: Sat Mar 14, 2009 6:20 pm

Re: Problem reading two sensors with analogRead()

by karlgg on Sun Jun 21, 2009 2:53 pm

Make sure you put a delay after both reads... If the loop back to the beginning is too quick, you'll just see read #2 ruining read #1, instead of the other way around.
I think I am, therefore I am... I think.
User avatar
karlgg
 
Posts: 212
Joined: Sat Dec 27, 2008 1:41 pm
Location: Anthony, NM

Re: Problem reading two sensors with analogRead()

by Len17 on Sun Jun 21, 2009 2:56 pm

Thanks fat16lib, your solution worked like a charm! I'm now getting consistent results reading either or both sensors.
User avatar
Len17
 
Posts: 393
Joined: Sat Mar 14, 2009 6:20 pm

Re: Problem reading two sensors with analogRead()

by ahdavidson on Mon Aug 24, 2009 3:57 pm

Would that same multiplexing problem apply to multiple pins of an accelerometer being read via analogRead ? In other words, I guess, are accelerometers high impedance?
.andy
User avatar
ahdavidson
 
Posts: 131
Joined: Wed Jun 03, 2009 8:59 am
Location: Seattle, WA

Re: Problem reading two sensors with analogRead()

by Zener on Mon Aug 24, 2009 5:15 pm

ahdavidson wrote:Would that same multiplexing problem apply to multiple pins of an accelerometer being read via analogRead ? In other words, I guess, are accelerometers high impedance?

Shouldn't be too high. However, even with a low impedance source, a multiplexed AD could still probably benefit from fat16lib's routine.
Zener
 
Posts: 2381
Joined: Sat Feb 21, 2009 1:38 am

Re: Problem reading two sensors with analogRead()

by bswift5528 on Sun Jan 31, 2010 5:33 pm

I was planning a temp sensor using a CD4051 8-channel analog multiplexer to pipe voltages from eight TMP36 temp sensors into a single analog pin on the Arduino. Do you think this will suffer the same problem as the LM35?

It's datasheet (http://www.ladyada.net/media/sensors/TMP35_36_37.pdf) says that it's "low output impedance", so I'm thinking no, but, then again, I'm a total noob... fatlib16 said in the original reply that "Temperature sensors must use low power and thus be high impedance to avoid IR heating." So how is it that the TMP3* series is low impedance, while still "providing low self-heating" (citing the datasheet again)???
bswift5528
 
Posts: 13
Joined: Tue Dec 09, 2008 5:00 pm

Re: Problem reading two sensors with analogRead()

by fat16lib on Sun Jan 31, 2010 7:49 pm

The ATmega ADC is suitable for devices with at most 10K ohm impedance (see the 168/328 data sheet). The Arduino analog library does not handle the ADC mux well so 10K ohms impedance won't work unless you use tricks like multiple reads and delays.

Good instrumentation amplifiers and ADCs have input impedance in the tens of mega-ohms to gig-ohms. The LM35 is low impedance in this context.
fat16lib
 
Posts: 591
Joined: Wed Dec 24, 2008 12:54 pm

Re: Problem reading two sensors with analogRead()

by bswift5528 on Sun Jan 31, 2010 8:02 pm

Soo...yes, I should use the delay in reading the TMP36 as well?
bswift5528
 
Posts: 13
Joined: Tue Dec 09, 2008 5:00 pm

Re: Problem reading two sensors with analogRead()

by fat16lib on Sun Jan 31, 2010 8:48 pm

The Arduino ADC is marginal for your application. I would use an external ADC/mux. In my last post I meant the LM35 is low impedance with a decent amp/ADC, not the Arduino's ADC.
fat16lib
 
Posts: 591
Joined: Wed Dec 24, 2008 12:54 pm

Re: Problem reading two sensors with analogRead()

by bswift5528 on Mon Jul 26, 2010 2:11 am

Ok, it's been quite a while since this thread was active, so I'm resurrecting it. :) A few months back I messed around with what I was inquiring about, had some problems, had to put it down for a time, and just started playing again the other day. The deal is that I wanted to hook up a bunch of LM35/TMP36 type of analog temperature sensors to the Arduino through a single analog read pin by way of a CD4051 multiplexer. You know, save pin space on the Arduino for other fun things...

I had used Ladyada's original Temperature sensor tutorial, though I see that it's changed now. A lot of my questions/findings are based on the last code chunk (that was) there where there was a bit about calibrating the sensor by measuring the bandgap reference pin #14. By the look of the tutorial now, it seems the thinking on that has changed but I haven't yet found out why. Maybe someone here (fat16lib?) can help us all understand what's going on, why the change was made, and why I'm seeing what I'm seeing?

The Setup:
The Arduino uses 3 digital pins set to output to address up to 8 different temperature sensors hooked up to the multiplexer. On my test board I have 3 sensors. During loop(), the board reads the voltage on reference pin 14, cycles through the addresses (001, 010, 011), setting them (on pins 5,6,7), and then reading the mux output on pin 0. During any pin read, I'm using a read-delay-reread-delay method based on earlier posts in this thread to try to avoid weird read impedance issues.

Problem:
I noticed almost right away that the "calibrated" temperature readings were waaaay too high to be correct. It seemed that if I introduced a long enough delay in the loop I could make the output seem more reasonable, but the temperatures it was now giving were a few degrees too low compared to an accurate thermometer on the desk right next to the sensors. Strangely, the "uncalibrated" temperatures agreed very well with the reference thermometer. Needless to say, I was puzzled (and still am) about why it was behaving that way.

Looking at the raw readings coming off of the Arduino ADC (ie, the 0-1023 value before it is converted to a voltage or temperature), the sensor pin reads were fairly constant and independent of the duration of the delay in the loop. Reading the bandgap reference pin is another matter, however. Reading that pin too quickly after switching from another pin (after a short loop delay) results in a lower value from the ADC, which is why the calibrated temperatures were coming out too high.

After discovering this, I changed the sketch, below, to start with a loop delay of 20 ms, and increase the delay by 20 ms each time through.
Code: Select all
#define BANDGAPREF 14   // special pin so we can measure the bandgap

int loopDelay        = 20;   // ms between loops
int pinReadDelay     = 10;   // ms delay to stabilize reading on Arduino ADC when switching pin-to-pin?
int muxAddPins[]     = { 5, 6, 7 };  // digital pins on Arduino that control all the muxes

int analogReadWithDelays(int pin)
{
  analogRead(pin);
  delay(pinReadDelay);
  int val = analogRead(pin);
  delay(pinReadDelay);
  return val;
}

float volt2temp(float volt)
{
  float tempC = (volt - 0.5) * 100; //Celcius
  return (tempC * 9. / 5.) + 32.;  //returns Fahrenheit
}


void setup()
{
  for (int ii = 0; ii < 3; ii++) {  //set multiplexer address switch pins to OUTPUT
    pinMode(muxAddPins[ii], OUTPUT);
  }
  Serial.begin(9600);  //Start the serial connection with the computer
  delay(2000);
}


void loop()   
{
  int sensorPin = 0;
  int addressbit[] = { 1,2,4 };  // for accounting/pin math
  int refReading, reading;
  float supplyVoltage, voltage, Uvoltage;
 
  Serial.print(loopDelay);Serial.print(" || "); //start line
 
  refReading = analogReadWithDelays(BANDGAPREF); //use this reference for all sensor reads
 
  // run through pin addresses on the mux (ie, 001, 010, 011 for the three on right now)
  for (int mpi = 0; mpi < 3; mpi++ ) {
    for (int pi = 3-1; pi>=0; pi--) { // set each address bit
      if ( mpi & addressbit[pi] ) { digitalWrite(muxAddPins[pi], HIGH); } else { digitalWrite(muxAddPins[pi], LOW); }
    }
    // Now read the voltage off the sensor at that mux address
    //   ...calculate our power supply voltage from the known 1.05 volt reading
    supplyVoltage = (1.05 * 1024) / refReading;
    //   ...get the voltage reading from the temperature sensor, convert that to voltage
    reading  = analogReadWithDelays(sensorPin);
    voltage  = reading * supplyVoltage / 1024;     
    Uvoltage = reading * 5.0 / 1024;  // also, look at "uncalibrated" version
   
    Serial.print(volt2temp(voltage),3); Serial.print("  ");
    Serial.print(volt2temp(Uvoltage),3);Serial.print(" | ");
  }
  Serial.println(); //end the line
 
  delay(loopDelay); //WHY DO I NEED THIS TO BE SO LONG???!!!

  loopDelay = loopDelay + 20; // in each iteration, add some time to the loop
}


The results of this sketch for loop delays of 20ms through 10,000ms are plotted below. The blue points are for the "uncalibrated" temperatures and the red points are for the "calibrated" temperatures. Additionally, each of the three individual sensors are represented by triangles, circles, and squares, plotted in that order. Remember that a comparison thermometer on the desk next to the sensors reads ~80F.

temptest.png
Actual temperature is ~80F.
temptest.png (68.56 KiB) Viewed 28866 times


I'll test this again soon to see if the "circle" sensor (actually, the 2nd sensor read of the 3) really always behaves that noisily for loop delays <~7000ms, or if it is just sporadically noisy. I noticed something weird about it earlier, so that may indeed be the case. Anyway, there it is.

So I still have my original questions, but I'm hoping we can use this to discuss what's going on. Why is it that switching to pin 14 too fast after reading another pin yields low values from the ADC (and high calibrated temps), and why doesn't this happen when going from pin 14 to another pin? Why were we trying to use pin 14 to calibrate the temperature reading in the first place if the temperatures are all off in the end and the uncalibrated temperature was correct? Why is that factor 1.05 there in the original post (and in my code) in the calibration formula? From what I can tell, the 8 or 9 degree difference, as well as the direction of the difference can be accounted for by that extra 0.05...

Any insight would be greatly appreciated! Thanks in advance!
-Brandon
Last edited by bswift5528 on Mon Jul 26, 2010 3:39 am, edited 1 time in total.
bswift5528
 
Posts: 13
Joined: Tue Dec 09, 2008 5:00 pm

Re: Problem reading two sensors with analogRead()

by bswift5528 on Mon Jul 26, 2010 3:36 am

Must just be a wonky sensor. This is much prettier...
temptest1.png
temptest1.png (53.16 KiB) Viewed 28866 times
bswift5528
 
Posts: 13
Joined: Tue Dec 09, 2008 5:00 pm

Re: Problem reading two sensors with analogRead()

by adafruit on Mon Jul 26, 2010 12:20 pm

yes the bandgap reference isnt stable, thats why the tutorial has changed. please go and reread the tutorial and adjust your code to match the new tutorial with 3.3v as the reference.
User avatar
adafruit
 
Posts: 11506
Joined: Thu Apr 06, 2006 3:21 pm
Location: nyc