0
\$\begingroup\$

I'm currently working on a hotplate project with an ESP32, utilizing a 10k ohm NTC thermistor (B57550G1103F000) capable of measuring temperatures up to 300°C. However, I'm encountering issues with temperature accuracy. Below 50°C, there's about 5°C discrepancy between the measured and actual temperatures. Strangely, as the temperature increases beyond 50°C, this discrepancy becomes significantly larger (can go up to 300°c discrepancy).

I've experimented with both 3.3V and 5V connected to the voltage divider circuit. With 3.3V, the accuracy improves at lower temperatures but worsens as the temperature rises. Conversely, with a 5V supply, the accuracy is better at higher temperatures but remains problematic overall.

Circuit Setup:My circuit

Here's the relevant portion of my code:

void checkTemperature() {
  int ThermistorPin = 34;  // Pin connected to the thermistor
  double adcMax = 4095.0;  // Maximum value of the ADC (analog-to-digital converter)
  double Vs = 5;           // Supply voltage

  double R1 = 10000.0;   // Voltage divider resistor value
  double Beta = 3478.0;  // Beta value of the thermistor
  double To = 298.15;    // Temperature in Kelvin for 25 degrees Celsius
  double Ro = 10500.0;   // Resistance of the thermistor at 25 degrees Celsius

  // Variables for calculations
  double Vout, Rt = 0;
  double T, Tc, Tf = 0;
  double totalTemperature = 0;  // Accumulator for summing up temperature readings

  // Read the temperature three times and calculate the average
  for (int i = 0; i < 3; i++) {
    // Read analog input and convert it to voltage
    Vout = analogRead(ThermistorPin) * Vs / adcMax;

    // Calculate resistance of the thermistor
    Rt = R1 * Vout / (Vs - Vout);
    // Calculate temperature using the Steinhart-Hart equation
    T = 1 / (1 / To + log(Rt / Ro) / Beta);  // Temperature in Kelvin

    // Convert temperature to Celsius
    Tc = T - 273.15;  // Celsius

    // Check if temperature is below 0 Celsius
    if (Tc <= 0) {
      // Re-read the temperature if it's below or equal to 0 Celsius
      Vout = analogRead(ThermistorPin) * Vs / adcMax;
      Rt = R1 * Vout / (Vs - Vout);
      T = 1 / (1 / To + log(Rt / Ro) / Beta);  // Temperature in Kelvin
      Tc = T - 273.15;                         // Celsius
    } else {
      // Add current temperature reading to the accumulator
      totalTemperature += Tc;
    }
  }

  // Calculate the average temperature over the three readings
  currentTemp = totalTemperature / 3;

  // Print the temperature
  Serial.print("Temperature: ");
  Serial.println(currentTemp);
  Serial.println(" °C");
}

I've linked the datasheet of the thermistor for reference: Thermistor Datasheet.

Digikey link to the thermistor: https://www.digikey.ca/en/products/detail/epcos-tdk-electronics/B57550G1103F000/3500386

I'd appreciate any insights into resolving this temperature accuracy issue. Thanks in advance!

\$\endgroup\$
4
  • \$\begingroup\$ Are you sure that the ADC reference voltage is the same as the supply voltage? Compare calculated Vout with measured Vout via DMM. \$\endgroup\$
    – Jens
    Commented Mar 29 at 1:00
  • \$\begingroup\$ Could you please show how the thermistor is physically connected to your ESP32, how it is shielded? When you take three measurements, how different they are? What kind of electrical filtering do you have on the ADC input? \$\endgroup\$ Commented Mar 29 at 1:14
  • \$\begingroup\$ I would start with the raw signal on the ADC convertor vs actual temperature. My guess is the calculation is wrong. But check to make sure garbage in isn't happening first. In other words, does the ADC return the expected bits for X temperature? \$\endgroup\$
    – MOSFET
    Commented Mar 29 at 2:37
  • \$\begingroup\$ Hi @Ale..chenski , the thermistor is directly connected to the esp32 (with the voltage divider) and it is not shielded. The three mesurement are usully close, but sometime they have a big difference. I have no filtering on the ADC input. \$\endgroup\$ Commented Mar 30 at 0:04

2 Answers 2

4
\$\begingroup\$

You have Vs = 5V, for starters. That would be correct for an Arduino with ADC using Vcc=5V as reference. The actual Espressif chip's ADC input has a range of about 0 to 1.1V (uncalibrated internal reference, not ideal for your application since you have a signal ratiometric to the nominal 3.3V Vdd), but I believe the boards usually have a voltage divider that reduces the input from about 3.3V to 1.1V (which doesn't help with the non-ratiometric measurement- errors in Vdd and in the reference and in the divider (if present) will add errors to your measurement that would not otherwise be there unless you individually calibrate those errors out.

Note that this divider, if present, loads your circuit (unlike the case with the ATmega328 chip). That will cause more error at low temperatures than at high since the impedance of your divider drops at the NTC thermistor is heated, unless it is compensated for in the calculations.

The ADC in the ESP32 is pretty horrible, even for free, and you should read up on it and the limitations, especially near 0V which is where your input will be at high temperature.

Apply the divide et impera technique- read the actual voltage with a multimeter and do calculations in a spreadsheet or whatever to figure out what is going on.

\$\endgroup\$
3
  • \$\begingroup\$ Do you know if external ADC exist? If yes, would that be a good option? \$\endgroup\$ Commented Mar 29 at 22:31
  • \$\begingroup\$ Certainly they exist. For example, there is an Arduino library for the 16-bit ADS1115, however it also has an inaccessible internal reference so ideally you'd measure both Vs and the divider voltage. There are other options. \$\endgroup\$ Commented Mar 30 at 7:45
  • \$\begingroup\$ Thanks! I'll consider these as a possible option! \$\endgroup\$ Commented Mar 30 at 15:24
3
\$\begingroup\$

Several issues:

Hardware: ensure that the thermistor is connected as shown, nothing else is loading down the node, or other sources of interference, there is a bypass capacitor near the pin to supply ADC sampling current or you make repeat acquisitions on the same port to ensure correct acquisition, and that supply, ADC reference and divider are connected and configured correctly.

Contrary to popular opinion, an ADC is not a voltmeter or voltage-measuring device. I don't mean it isn't, or can't, but there is a deeper truth: it is a ratio measuring device. When you take a sample, the result is a dimensionless value in the output range. Typically right-aligned so the value can be understood as multiplied by MAXVAL+1, or left-aligned and understood as a fixed-point 0.N fractional value.

Once that range is multiplied by a calibration constant representing Vref, we have a voltage measurement. That is, there is one additional step -- it might be a trivial step, but nonetheless.

We don't need to perform that calibration step if we are making a ratiometric measurement in the first place. If the resistor divider is supplied by Vref, Vref cancels out, and only needs to be high enough and stable enough to meet ADC specs; its exact value is irrelevant.

You don't show where and how the ADC is connected, so I am unable to offer further insight in this, only to note how it must be done.

Conversion: Steinhart-Hart is often [mis]presented as factual and exact for NTCs; it is not, it is only a convenient curve-fitting kernel, and is empirical in nature. The 1st order form is probably the most common, but it is merely one of a family of higher-order functions that just happen to fit this family of semiconductor devices well.

Plotting the manufacturer's data against the function given, gives this error function:

enter image description here

(Axes in °C)

Your mid-range error will eventually be eclipsed by this approximation error. 1st-order S-H approximation is best used for narrow ranges, such as the 0-70°C commercial range, or even less.

We can simplify calculations, avoiding the floating-point operations that are typically quite burdensome on embedded platforms (I don't know whether this is the case for your ESP32, but probably not if you're using Arduino), using a polynomial series operating on the raw ADC reading instead.

I've automated many of these steps on my site:
Thermistor Data and Functions | Calculators | Seven Transistor Labs, LLC
Entering manufacturer's data (series 8307) into a file,

Manufacturer,TDK
Part Number,B57550G1103F000
Nominal Resistance,10000
Temperature,Resistance
-55,526240
-50,384520
-45,284010
-40,211940
-35,159720
-30,121490
-25,93246
-20,72181
-15,56332
-10,44308
-5,35112
0,28024
5,22520
10,18216
15,14827
20,12142
25,10000
30,8281.8
35,6895.4
40,5770.3
45,4852.5
50,4100
55,3479.8
60,2966.3
65,2539.2
70,2182.4
75,1883
80,1630.7
85,1417.4
90,1236.2
95,1081.8
100,949.73
105,836.4
110,738.81
115,654.5
120,581.44
125,517.94
130,462.59
135,414.2
140,371.79
145,334.51
150,301.66
155,272.64

and parsing it with the tool gives excellent fitness (<1°C error, as accurate as you can expect from a 1% thermistor) for a 5th order fit from -30 to 105°C:

/**
 *  Custom Data divider, 1kΩ pullup, ADC gain 1,
 *  T = [-30, 105]°C, order 5, descending sequence
 */
const int COEFFICIENTS[] = {
    -24612, 16604, -17836, 19177, -24200, 19212
};
const int SHIFTS[] = {
    -2, 0, 1, 2, 2, -3
};
//  Max accum bits used: 31

int countToTemp(int arg) {

    int i;
    int accum = COEFFICIENTS[0];

    for (i = 0; i < numelem(COEFFICIENTS) - 1; i++) {
        accum *= arg;
        accum /= (1 << (16 - SHIFTS[i]));
        accum += COEFFICIENTS[i + 1];
    }
    i = numelem(SHIFTS) - 1;
    if (SHIFTS[i] > 0)
        accum <<= SHIFTS[i];
    else if (SHIFTS[i] < 0)
        accum /= (1 << (-SHIFTS[i]));

    return accum;
}

which returns a fixed-point (12.4) result.

If you expect operation out to 300°C, I would strongly encourage using an RTD or thermocouple. The resistance change of an RTD is narrower, but far more precise; the resulting offset can be subtracted out with an op-amp, needing less precision (dynamic range) in the ADC than reading the divider directly. For thermocouples, you will need more gain, and a precision op-amp; some means of cold-junction compensation is also welcome, or you can use an integrated (usually digital output) thermocouple receiver.

\$\endgroup\$
3
  • \$\begingroup\$ Can you explain what you mean with "You don't show where and how the ADC is connected, so I am unable to offer further insight in this, only to note how it must be done." ? I will be happy to provide more info about my wiring and etc! \$\endgroup\$ Commented Mar 29 at 22:34
  • \$\begingroup\$ We would've needed schematics and a wiring diagram -- FYI, these are good to assume as required, for any question, to get the best answers in reply. \$\endgroup\$ Commented Mar 30 at 1:36
  • \$\begingroup\$ For the wiring diagram, the "Thermistorpin" pin in the picture is directly connected to the pin 34 of the ESP32 \$\endgroup\$ Commented Mar 30 at 20:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.