TMP36 USB2 USB3 voltage different

Hi

I just purchased a starter kit and am working on project 3. I follow the book and use their mathematic formula to translate sensor readings to voltage.

However, I realise the sensor (or maybe the entire setup) gives different reading when plugged into either USB2 on any laptop v.s. USB3 on desktops. The temperature is about 1 degree C higher on USB3; sensor reading is also higher on USB3.

I reckon this might be due to USB3 supplying a higher voltage to the Adruino than USB2? I read a few threads on similar issues. Some of them talked about using the analogReference(). But honestly I have no idea how to implement this function. For example, if I use analogReference(INTERNAL) I get some absurd readings.

Is there a better way to ensure accuracy if my plan is to plug in the project into different power supply? Since I have no idea where will I be using it in the future.

Here is my code

#include <arduino.h>

#if defined(USBCON)
  USBDevice.attach();
#endif

auto setup() -> void;
auto led_Logic(const float &temperature) -> void;
auto ONOFF(const int &pin3 = LOW, const int &pin4 = LOW, const int &pin5 = LOW) -> void;

static const int g_sensorPin{A0};
static const float g_baselineTemp{20.0};

int main()
{ 
  init();
  setup();
  int sensorVal;
  int &sensorRef{sensorVal};
  float voltage;
  float &voltageRef{voltage};
  float temperature;
  float &temperatureRef{temperature};
  
  while (true)  
  {
    sensorRef = analogRead(g_sensorPin);
    
    Serial.print("Sensor Value: ");
    Serial.print(sensorRef);

    voltageRef = ( sensorRef/1023.0) * 5.0; 
    Serial.print(", Volts: ");
    Serial.print(voltageRef);

    temperatureRef = ( voltageRef - .5) * 100;
    Serial.print(", degrees C: ");
    Serial.println(temperatureRef);
    
    led_Logic(temperatureRef);
    delay(1);
  };
  
  return 0;
};

auto setup() -> void
{
  Serial.begin(9600);
  int firstLEDPin{3};
  int lastLEDPin{5};
  for(int pinNumber{firstLEDPin}; pinNumber <= lastLEDPin; ++pinNumber)
  {
    pinMode(pinNumber, OUTPUT);
    digitalWrite(pinNumber, LOW); 
  }
}

auto led_Logic(const float &temperature) -> void
{
  if (temperature < g_baselineTemp) {
    ONOFF(); //all LEDs off
  } else if (temperature < g_baselineTemp + 2){
    ONOFF(HIGH); //first LED on
  } else if (temperature < g_baselineTemp + 4) {
    ONOFF(HIGH, HIGH); //first two LEDs on
  } else if (temperature < g_baselineTemp + 6) {
    ONOFF(HIGH, HIGH, HIGH); //all LEDs on
  } else {
    ONOFF(HIGH, HIGH, HIGH); //Flashing light.
    delay(200);
    ONOFF();
    delay(199); //already a 1 millisecond delay at the end of main
  }
}

auto ONOFF(const int &pin3, const int &pin4, const int &pin5) -> void
{
  digitalWrite(3, pin3);
  digitalWrite(4, pin4);
  digitalWrite(5, pin5);
}

Don't over-complicate things.
One line of code is all you need.
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 decimal place
  Serial.print(" C");
  delay(1000); // use a non-blocking delay when combined with other code
}

Edit:
In case you wonder why the 0.1039 This, simplified: Aref*100/1024
See this page.
Every Arduino has a sligtly different uncalibrated (but stable) 1.1volt Aref.
My Uno was a bit on the low side (~1.064volt).

Wawa:
Don't over-complicate things.
One line of code is all you need.
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 decimal place
  Serial.print(" C");
  delay(1000); // use a non-blocking delay when combined with other code
}




Edit:
In case you wonder why the 0.1039 This, simplified: Aref*100/1024
[See this page.](https://playground.arduino.cc/Main/LM35HigherResolution)
Every Arduino has a sligtly different uncalibrated (but stable) 1.1volt Aref.
My Uno was a bit on the low side (~1.064volt).

wawa

Thank you for the quick reply.
I'm now confused by the math. So now I'm using the 3.3V output. What would the formula be if I want to get the reading of the voltage as well? Will it include (Aref /1024) * 100 (assuming my Aref is 1.1v exactly)?
Also, how did you get the exact Aref value of your Arduino? And what is a non-blocking delay?

Thanks

dants:
So now I'm using the 3.3V output.

You don't have to.
A TMP36 can be powered from the 3.3volt pin or the 5volt pin (2.7-5.5volt range).
But the 3.3volt supply pin of an Uno is potentially 'cleaner', and much less dependent on variations in USB power.
Power for the TMP36 has nothing to do with it's temp output voltage.
Which is 10mV per degree C, plus a 500mV offset.

Not sure why you want to measure USB/MCU supply voltage (5volt rail).
The Uno works reliably between 3.85volt and 5.5volt.
Voltage measurement only makes sense if you battery-power the Uno (measure the battery).
Then you would use a voltage divider, to bring battery voltage down to ~1volt.
And measure against the same ~1.1volt Aref.
Formula would then also depend on the ratio of that voltage divider.

delay() is blocking code. The processor just waits there until the delay has passed.
Not a problem for the example sketch, because nothing else needs to be done.
The IDE has a BlinkWithoutDelay example that shows you how to have delays without stopping the loop.
Leo..

wawa

I want to know the voltage because I will be using a similar setup with different hardware in the future. I will be connecting the Arduino with some pressure sensors for research purposes. Being able to calculate the voltage going through the components helps calibration.

I have two more questions

  • Under the same ambient temperature, the value returned by analogRead(TMP36 pin) is about 5 times bigger when using the 3.3V pin than using the 5V pin. I went through the documentation on the TMP36, and still don't understand how different voltage input affects the reading? Shouldn't a lower voltage input produce lower readings?
  • Currently, I'm calculating the voltage using the formula sensorValue * (1.1/1024). However, because the TMP36 uses a 0.5V offset value, should I subtract 0.5 here to get the voltage?
  • How to obtain the exact 1.1volt Aref value for my Arduino? 1.1 is a little too high for my Arduino; your value of 1.064 does produce more accurate readings on my setup but I want to be more precise.

Thank you

Here is my updated code

#include <arduino.h>

#if defined(USBCON)
USBDevice.attach();
#endif

auto setup() -> void;
auto led_Logic(const float &temperature) -> void;
auto ONOFF(const int &pin3 = LOW, const int &pin4 = LOW, const int &pin5 = LOW) -> void;

static const byte  g_sensorPin = A0;                           
static const int   g_ADC_Range{1023};
static const float g_Aref{1.10};                                
static const float g_calibration = (g_Aref/g_ADC_Range) * 100;  // Calibration = (Aref/1024) * 100. Not used here but remain for future reference,

static const float g_baselineTemp{25.0};
const long interval{1000};

unsigned long previousMillis{0};      
bool ledState{false};

int main()
{
  init();
  setup();
  int sensorVal;
  int &sensorRef{sensorVal};
  float voltage;
  float &voltageRef{voltage};
  float temperature;
  float &temperatureRef{temperature};

  while (true)
  {
    sensorRef = analogRead(g_sensorPin);
    Serial.print("Sensor Value: ");
    Serial.print(sensorRef);

    voltageRef = sensorRef * ( g_Aref / g_ADC_Range); // e.g. divide 1.1V over 1023, each step up in the analog reading is equal to approximately 0.001075V, or 1.0752 mV.
    Serial.print(", Volts: ");
    Serial.print(voltageRef);

    temperatureRef = ( (voltageRef  - 0.5) * 1000 / 10);// voltage * 1000 to get mV; 
                                                        // divide by 10 for each degree C change (the TMP36 changes 10 mV per 1 degree C change)
                                                        // the TMP36 uses a 0.5V offset voltage
    Serial.print(", degrees C: ");
    Serial.println(temperatureRef);
    
    led_Logic(temperatureRef);
    delay(1);
  };

  return 0;
};

auto setup() -> void
{
  Serial.begin(9600);
  analogReference(INTERNAL);                    
  int firstLEDPin{3};
  int lastLEDPin{5};
  for (int pinNumber{firstLEDPin}; pinNumber <= lastLEDPin; ++pinNumber)
  {
    pinMode(pinNumber, OUTPUT);
    digitalWrite(pinNumber, LOW);
  }
}

auto led_Logic(const float &temperature) -> void
{
  if (temperature < g_baselineTemp) {
    ONOFF(); //all LEDs off
  } else if (temperature < g_baselineTemp + 2) {
    ONOFF(HIGH); //first LED on
  } else if (temperature < g_baselineTemp + 4) {
    ONOFF(HIGH, HIGH); //first two LEDs on
  } else if (temperature < g_baselineTemp + 6) {
    ONOFF(HIGH, HIGH, HIGH); //all LEDs on
  } else {
    if (millis() - previousMillis >= interval) 
    {
      previousMillis = millis();
      ledState = !ledState;
      ONOFF(ledState, ledState, ledState);
      return;
    } 
  }
  previousMillis = 0; //Only reset if not above 6 degree C.
}

auto ONOFF(const int &pin3, const int &pin4, const int &pin5) -> void
{
  digitalWrite(3, pin3);
  digitalWrite(4, pin4);
  digitalWrite(5, pin5);
}

dants:
I want to know the voltage because I will be using a similar setup with different hardware in the future. I will be connecting the Arduino with some pressure sensors for research purposes. Being able to calculate the voltage going through the components helps calibration.

Most (not all) pressure sensors have a ratiometric output, in which case output voltage is not relevant.

dants:
Under the same ambient temperature, the value returned by analogRead(TMP36 pin) is about 5 times bigger when using the 3.3V pin than using the 5V pin.

Something fishy going on. TMP36 output voltage should not depend on it's power supply voltage.
Changing Aref from DEFAULT (~5volt) to INTERNAL (~1.1volt) ofcourse makes the A/D about 5x more sensitive, and results in a ~5x higher A/D value.

dants:
Currently, I'm calculating the voltage using the formula sensorValue * (1.1/1024). However, because the TMP36 uses a 0.5V offset value, should I subtract 0.5 here to get the voltage?

The TMP35 has a 50 degrees C (500mV) offset.
Subtract 50 degrees or 500mV, depending if you are doing temp or voltage calculation.

dants:
How to obtain the exact 1.1volt Aref value for my Arduino? 1.1 is a little too high for my Arduino; your value of 1.064 does produce more accurate readings on my setup but I want to be more precise.

If you don't want to tinker, then dump that analogue TMP36 sensor.
A digital DS18B20 is connect and forget.
Leo..