Difference in TMP36 readings between R3 and R4

I have a plain simple circuit with just a TMP36 connected to A0, power (5V) and GND.

The sketch is dead simple, too:

void loop() {
  int sensorVal = analogRead(sensorPin);
  Serial.print("Sensor Value: ");
  Serial.print(sensorVal);

  float voltage = (sensorVal*5.0)/1024.0;
  Serial.print(", Volts: ");
  Serial.print(voltage);

  Serial.print(", degrees C: ");
  float celsius = (voltage - 0.5) * 100;
  Serial.println(celsius); 

  delay(30000);
}

When working with R3 I get the following readings:

Sensor Value: 138, Volts: 0.67, degrees C: 17.38
Sensor Value: 138, Volts: 0.67, degrees C: 17.38
Sensor Value: 137, Volts: 0.67, degrees C: 16.89

When I switch to R4 the readings are as follows:

Sensor Value: 177, Volts: 0.86, degrees C: 36.43
Sensor Value: 177, Volts: 0.86, degrees C: 36.43
Sensor Value: 177, Volts: 0.86, degrees C: 36.43
Sensor Value: 176, Volts: 0.86, degrees C: 35.94

This is strange. The readings from R3 are correct while those from R4 are way off.

Any ideas why this is happening?

Have you tried explicitly setting the analogue read resolution on the R4 ?

analogReadResolution(10)

No. Can you elaborate? The default should've been fine, shouldn't it? Thanks.

Yes, the default should be fine, but there is no harm in trying to set it explicitly

Sorry, that's not it.

Have you tried putting the same voltage level simultaneous at both pins. For example with a potentiometer?

That is what I would expect, the A/Ds are different between the R3 and R4. That difference needs to be taken care of in your coding. This may help: https://docs.arduino.cc/tutorials/uno-r4-minima/cheat-sheet Think of it this way, you want to get to the 10th floor and have a choice between the stairs, elevator and escalator, all will get you there but by a different route (code).

Yes.

A few things are going on.

I rigged up an R3 and R4 with LM35 temperature sensors I had on hand and an OLED display. The R4 ADC readings were consistently higher than the R3, resulting in the R4 reading ~ 8 degrees higher than the R3, even though the voltages measured at the sensor outputs were quite similar.

Adding a 0.1uF cap across the sensor's Vo to ground (making the input low impedance and capable of very quickly charging the ADC's internal cap) brought the R4 much closer to the R3's reading.

Upon measuring 5V on the R3 and R4, I noticed that the R3 was reading 4.99V whereas the R4 was reading 4.70V. Multiplying the ADC reading by 4.7 as opposed to 5.0V brought the R4 right in line with the R3.

Now, that 4.70V on the 5V line was measured with the R4 being powered by the USB connector. Using an external supply through the barrel connector, I got 4.98V on the R4's 5V line, and it was no longer necessary to adjust the 5.0 multiplier.

1 Like

That seems to work. Thanks a lot.

Is there any means of checking the board version or is it expected to have different code per board version?

1 Like

It's not a version issue.

You made the assumption that Vcc will always be exactly 5.000V and now you know it isn't.

Calibration is a possible answer, I guess.

Always!

1 Like

Proving you should never use the default 5volt reference to measure the output of an LM35/TMP36.

Always use one of the more stable internal references of the Uno. Then you don't have that problem of re-calibrating when using a different way of powering the setup. And you have a higher temp resolution of 0.1C instead of 0.5C (two decimal places is impossible with a TMP36/Uno).

Test sketch for the R3 attached. Not sure which Aref voltages the R4 has.
Leo..

const byte tempPin = A0; // connect TPM36 to 3.3volt A0 and (not-shared) ground
float calibration = 0.1039; // calibrate temp by changing the last digit(s) of "0.1039"
float tempC;

void setup() {
  Serial.begin(9600);
  analogReference(INTERNAL); // use internal 1.1volt Aref
}

void loop() {
  tempC = (analogRead(tempPin) * calibration) - 50.0;
  Serial.print("Temperature:  ");
  Serial.print(tempC, 1); // one correct decimal place is all you get
  Serial.print(" C");
  delay(1000); // use a non-blocking delay when combined with other code
}
1 Like

Wether you use external ref, internal ref or Vcc you still need to calibrate

I believe the UNO4's AREF is 1.5V but check with your DMM, try my "no floats, no delays" sketch, works with TMP36 and LM35, has calibration offset (calValue), smoothing and anti jitter.

/*
 LM35, TMP36 thermometer, no floats, no delays
*/
const bool t36 = true; // set false for LM35, true for TMP36
const byte numSamples = 8, // number of samples for smoothing
           aInPin = A0; // analog input pin
const int calValue = 0, // adjust for calibration, +/-0.1 degree
          hAref = 110, // analog ref voltage * 100
                       // measured with accurate DMM
          hnumSamples = numSamples * 100,
          tEnd = 3000; // update time in mS
int val,
    tempC,
    tempF;
uint32_t total,  // sum of samples
         tStart; // timer start
byte cnt = 10;
        
const char header[] = "\nRaw    Total   Temp C  Temp F";
         
void setup()
{
  Serial.begin(9600);
  analogReference(INTERNAL); // use 1.1V internal ref
  analogRead(aInPin);
  for(int i = 0;i < numSamples;i++) // for smoothing, fill total
    total += analogRead(aInPin);   // with numSamples * current
                                   // reading
}
void loop()
{
  if(millis() - tStart > tEnd)
  {
    tStart = millis(); // reset timer
    if(++cnt >= 10)
    {
      Serial.println(header); // every 10 lines
      cnt = 0;
    }
    val = analogRead(aInPin);
    total -= (total / numSamples); // make room for new reading
    total += val; // add new reading
    tempC = total * hAref / hnumSamples + calValue;
    if(t36)
      tempC -= 500;
    tempC = antiDither(tempC);
    tempF = tempC * 9 / 5 + 320;
    Serial.print(val);
    Serial.print("\t");
    Serial.print(total); // sum of samples
    Serial.print("\t");
    prntTemp(tempC);
    prntTemp(tempF);
    Serial.println();
  }
}
void prntTemp(int temp)
{
  Serial.print(temp / 10); // whole degrees
  Serial.print(".");
  Serial.print(temp % 10); // tenths
  Serial.print("\t");
}
int antiDither(int newVal) // prevents +1 <-> -1 dither
{                       
  static int val = 0, oldVal = 0;
  if(newVal != val && newVal != oldVal)
  {
    oldVal = val;
    val = newVal;
  }
  return val;
} 

The big difference is that with an internal reference you only need to do that once.
With default Aref you need to do this continuously.
Leo..

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