Skip to content

Measuring Temperature with NTC Thermistor 10K

For my final project, one of the input devices that I will use is temperature sensor. I will use this to measure the electrolyte liquid temperature for our hydrogen research. I was then referred by my instructor to a youtube tutorial where the guy made a DIY EC-TDS-Temperature Sensor Probe by himself. The temperature sensor being used there is the NTC Thermistor 10k which I will explore in this assignment.

NTC Thermistors

Temperature sensors come in various types, each with unique characteristics, suited for different applications. One of them is thermistors. Thermistors are temperature-sensitive resistors. Their resistance changes significantly with temperature. There are two types of thermistors: NTC (Negative Temperature Coefficient) and PTC (Positive Temperature Coefficient). NTC (Negative Temperature Coefficient) thermistors are a type of temperature sensor whose resistance decreases as the temperature increases.

How does it work?

NTC thermistors are made from semiconductor materials. When the temperature increases, the number of charge carriers (electrons and holes) in the semiconductor material increases, leading to a decrease in electrical resistance. This inverse relationship between temperature and resistance is what defines NTC thermistors. Some characteristics of NTC thermistors are:

  • Relationship between resistance and temperature: The resistance of an NTC thermistor decreases exponentially with an increase in temperature. This relationship can be calculated/expressed by the Steinhart-Hart equation which does a good approximation of converting resistance values of an NTC to temperature. However, the calculation is quite complex. But fortunately there is a simplification of this formula, called the B-parameter equation.
  • Sensitivity: NTC thermistors are highly sensitive to temperature changes, making them suitable for precise temperature measurements, such as in digital thermometer.
  • Temperature Range: They typically operate within a temperature range of -50°C to 150°C, although specialized types can go beyond this range.
  • Tolerance and Accuracy: NTC thermistors are available with various tolerances and can be highly accurate. However, accuracy can be affected by self-heating if not properly managed.

In general, an NTC thermistor is highly sensitive and accurate, cost-effective, and has a fast response time, making it ideal for precise temperature measurements. However, it has a non-linear resistance-temperature relationship and can suffer from self-heating, which may affect accuracy.

NTC 10K Thermistor 3435 +-1%

For my final project, the type of NTC Thermistor that I’m going to use is the NTC 10K Thermistor 3435 +/-1%. So, what does all this technical name means? Well, based on this Datasheet, I learned that:

  • The “10k” in the name indicates the resistance value at a reference temperature, typically 25°C or 298.15K. For a 10k NTC thermistor, the resistance at 25°C is 10,000 ohms.
  • The “3435” indicates the beta value (β) of the NTC thermistor. The beta value is a constant (in the B-parameter equation I mentioned above) that describes the relationship between the resistance of the thermistor and the temperature. The idea is, if you know the resistance of the thermistor and the beta value, you can actually calculate the estimated temperature.
  • 1%” indicates the tolerance of the beta value. This means the actual beta value can vary by 1% from 3435.

The specifications of the NTC 10K 3435 temperature sensing element:

specification

And here in this table, we can actually see the relationship between the resistance and the temperature, if measured with the Steinhart-Hart or B-parameter equation.

temperature-resistance

Note: The datasheet information above is actually about the NTC Thermistor component itself. But here, I’m going to use the one that’s already designed as a waterproof probe module. So, the real range of working temperature of the sensor might not be exactly the same.

Workflow: Measuring Temperature with NTC 10K Thermistor 3435

Setting up Hardware

Components

  • NTC 10K Thermistor
  • 10K ohm resistor 1/4 watt
  • XIAO RP2040

Technical Specs/Requirement

NTC thermistor 10k 3435 +-1% probe:

From the supplier page, here’s the technical specs of the sensor probe:

  • Input Voltage: 3.0 - 5.5V
  • Temperature Range: -30 -110°C
  • Accuracy: ±1%
  • Wire: 2-wires (non-polar)
  • Cable Length: 1 m
  • Enclosure: Waterproof 4 x 30 mm

XIAO RP2040

  • Operating Voltage: 3.3V
  • For general I/O pins: Working voltage of MCU is 3.3V. Voltage input connected to general I/O pins may cause chip damage if it’ higher than 3.3V.

Apparently, there is limitation on the XIAO RP2040 regarding the voltage input of its I/O pins. Initially, I didn’t pay close attention to these specifications and requirements. I simply followed the wiring instructions from the youtube tutorial, which used an Arduino UNO that can handle 5V voltage input.

Because of this oversight, I encountered problems with inaccurate readings–negative temperature results. I will show and discuss these issueslater in the document.

Circuit Connection

The thermistor needs to be part of a voltage divider circuit to convert the resistance change into a voltage change that can be read by the ADC (Analog-to-Digital Converter) on the XIAO.

circuit diagram

Pin configuration:

  • One end of thermistor –> 10K resistor –> GND on XIAO RP2040
  • The junction of the thermistor and resistor –> Analog pin (A2) on XIAO RP2040
  • The other end of NTC Thermistor –> Supply voltage (3.3V)

In my initial developments, I connected the other end of the thermistor to 5V pin. While it worked and generate temperature readings, the result appears in negative. Later I learned that it’s because of the gpio voltage input requirement of the XIAO can handle maximum 3.3V. And I also learn from this page that turns out for accurate measurements it is better to use the 3.3Volt line as analog reference and feed the resistor from there.

Programming

For starters, I used the code from the YouTube tutorial. The code is designed to read and measure EC TDS reading functionalities with temperature reference for calibration. I didn’t make any changes to the code, including the temperature reading part, except to adapt it to my other input and output devices.

Here, I will show you the temperature measurement parts only - that I extracted from the code and explain what is happening in the code.

Definitions and Variables

int Temp_pin = A2; // analog pin where the temperature sensor is connected

float Temp_C; // variable to store temperature values in Celsius
float Temp_F; // variable to store temperature values in Fahrenheit
float Temp1_Value = 0; // Intermediate variable to store temperature valuein celcius

Setup function

void setup() {
    Serial.begin(9600); // Initializes serial communication at 9600 baud.
}
Initializes serial communication at 9600 baud.

Loop function

void loop() {
    ReadTemperature();
    delay(6000); // Wait for 6 seconds before the next reading
}

Continuously calls ReadTemperature every 6 seconds to read and display the temperature.

Custom function

In the actual code from the youtube tutorial. The temperature reading is put under “GetEC” custom function as the reading is used as a reference for EC and TDS reading. But here, I appropriate the code only for reading temperature:

void ReadTemperature() {
    int val;
    double Temp;

    // Reads the analog value from the temperature sensor
    val = analogRead(Temp_pin);

    // Converts the raw value to temperature in Celsius using the Steinhart-Hart Thermistor Equation 
    Temp = log(((10240000 / val) - 10000));
    Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp)) * Temp);

    // Conversion to Kelvin and Fahrenheit
    Temp_C = Temp - 273.15; // Kelvin to Celsius conversion
    Temp_F = (Temp_C * 9.0) / 5.0 + 32.0; // Celsius to Fahrenheit
    Temp1_Value = Temp_C;

    // Display in Serial Monitor
    Serial.print("Temperature: ");
    Serial.print(Temp_C);
    Serial.print(" °C, ");
    Serial.print(Temp_F);
    Serial.println(" °F");
}
  1. Reads the analog value from the temperature sensor.
  2. Converts the raw sensor value to temperature in Celsius using the Steinhart-Hart Thermistor Equation
  3. Converts the temperature from Celsius to Fahrenheit.
  4. Prints the temperature values in Celsius and Fahrenheit to the serial monitor.

If you want to understand more about the calculation, I think this page does a great job in explaining the use Steinhart-Hart Equation for temperature reading with NTC thermistor.

Integrated Code: Temperature Reading

int Temp_pin = A2; // analog pin where the temperature sensor is connected
float Temp_C; // variable to store temperature values in Celsius
float Temp_F; // variable to store temperature values in Fahrenheit
float Temp1_Value = 0; // Intermediate variable to store temperature valuein celcius

void setup() {
    Serial.begin(9600); // Initializes serial communication at 9600 baud.
}

void loop() {
    ReadTemperature();
    delay(6000); // Wait for 6 seconds before the next reading
}

void ReadTemperature() {
    int val;
    double Temp;

    // Reads the analog value from the temperature sensor
    val = analogRead(Temp_pin);
    // Converts the raw value to temperature in Celsius using the Steinhart-Hart Thermistor Equation 
    Temp = log(((10240000 / val) - 10000));
    Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp)) * Temp);

    // Conversion to Kelvin and Fahrenheit
    Temp_C = Temp - 273.15; // Kelvin to Celsius conversion
    Temp_F = (Temp_C * 9.0) / 5.0 + 32.0; // Celsius to Fahrenheit
    Temp1_Value = Temp_C;

    // Display in Serial Monitor
    Serial.print("Temperature: ");
    Serial.print(Temp_C);
    Serial.print(" °C, ");
    Serial.print(Temp_F);
    Serial.println(" °F");
}

Results

The reading results I show you below use the initial source code (integrated with EC and TDS reading) which I appropriate it for my context. It includes the temperature reading program, as mentioned above.

Initial Test #1: inaccurate result (negative value)

In this test, I’m still connecting the thermistor to 5V pin on XIAO RP2040

physical circuit

As you can see, the temperature value number is sensible, the water is at room’s temperature (air-conditioned), it’s just that it was shown in negative.

Code used: EC, TDS, Temperature Reading (displayed in Serial Monitor)

    int R1= 1000; // Value of resistor for EC probe
    int EC_Read = A0;
    int ECPower = A1;
    int Temp_pin = A2;

    float Temp_C; // Do not change
    float Temp_F; // Do not change
    float Temp1_Value = 0;
    float Temp_Coef = 0.019; // You can leave as it is

    //This part needs your attention during calibration only//
    float Calibration_PPM =1080 ; //Change to PPM reading measured with a separate meter
    float K= 24.09; //You must change this constant once for your probe(see video)
    float PPM_Con=0.5; //You must change this only if your meter uses a different factor
    ////

    float CalibrationEC= (Calibration_PPM*2)/1000;
    float Temperature;
    float EC;
    float EC_at_25;
    int ppm;
    float A_to_D= 0;
    float Vin= 5;
    float Vdrop= 0;
    float R_Water;
    float Value=0;

    void setup() {
        Serial.begin(9600);
        pinMode(EC_Read,INPUT);
        pinMode(ECPower,OUTPUT);

    // Calibrate (); // After calibration put two forward slashes before this line of code

    }
    void loop() {
        GetEC(); //Calls GetEC()
        delay(6000); //Do not make this less than 6 sec (6000)
    }

    void GetEC() {
        int val;
        double Temp;

        val = analogRead(Temp_pin);
        Temp = log(((10240000/val) - 10000));
        Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
        Temp_C = Temp - 273.15; // Kelvin to Celsius
        Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
        Temp1_Value = Temp_C;
        Temperature = Temp_C;

        digitalWrite(ECPower,HIGH);
        A_to_D= analogRead(EC_Read);
        A_to_D= analogRead(EC_Read);
        digitalWrite(ECPower,LOW);
        Vdrop= (Vin*A_to_D) / 1024.0;
        R_Water = (Vdrop*R1) / (Vin-Vdrop);
        EC = 1000/ (R_Water*K);
        EC_at_25 = EC / (1+ Temp_Coef*(Temperature-25.0));
        ppm=(EC_at_25)*(PPM_Con*1000);

        Serial.print(" EC: ");
        Serial.print(EC_at_25);
        Serial.print(" milliSiemens(mS/cm) ");
        Serial.print(ppm);
        Serial.print(" ppm ");
        Serial.print(Temperature);
        Serial.println(" *C ");
    }
    ////////////////////////////////////////////////////////////////////////////////////
    void Calibrate () {
        Serial.println("Calibration routine started");
        float Temperature_end=0;
        float Temperature_begin=0;
        int val;
        double Temp;
        val=analogRead(Temp_pin);

        Temp = log(((10240000/val) - 10000));
        Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
        Temp_C = Temp - 273.15; // Kelvin to Celsius
        Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
        Temp1_Value = Temp_C;
        Temperature_begin=Temp_C;
        Value = 0;
        int i=1;
        while(i<=10){
        digitalWrite(ECPower,HIGH);
        A_to_D= analogRead(EC_Read);
        A_to_D= analogRead(EC_Read);
        digitalWrite(ECPower,LOW);
        Value=Value+A_to_D;
        i++;
        delay(6000);
    };
        A_to_D=(Value/10);
        val=analogRead(Temp_pin);
        Temp = log(((10240000/val) - 10000));
        Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
        Temp_C = Temp - 273.15; // Kelvin to Celsius
        Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
        Temp1_Value = Temp_C;
        Temperature_end=Temp_C;
        EC =CalibrationEC*(1+(Temp_Coef*(Temperature_end-25.0)));
        Vdrop= (((Vin)*(A_to_D))/1024.0);
        R_Water=(Vdrop*R1)/(Vin-Vdrop);
        float K_cal= 1000/(R_Water*EC);
        Serial.print("Replace K value with K = ");
        Serial.println(K_cal);
        Serial.print("Temperature difference start to end were = ");
        Temp_C=Temperature_end-Temperature_begin;
        Serial.print(Temp_C);
        Serial.println("*C");
        Serial.println("Temperature difference start to end must be smaller than 0.15*C");
        Serial.println("");
        Calibrate ();
    }
    ////////////////////////////////////////////////////////////////////////////////////

Initial Test #2: still inaccurate result (negative value)

Still showing negative value, but I want to show you here is the fact that in this test I put some ice on the water, and the temperature value number corresponds with it eventhough still negative.

Code: EC, TDS, Temperature Reading (displayed in OLED I2C SSD1306 0.96”)

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 2

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int R1= 1000; // Value of resistor for EC probe
int EC_Read = A0;
int ECPower = A1;
int Temp_pin = A3;

float Temp_C; // Do not change
float Temp_F; // Do not change
float Temp1_Value = 0;
float Temp_Coef = 0.019; // You can leave as it is
/////////////////This part needs your attention during calibration only///////////////
float Calibration_PPM =380 ; //Change to PPM reading measured with a separate meter
float K= 10.18; //You must change this constant once for your probe(see video)
float PPM_Con=0.5; //You must change this only if your meter uses a different factor
/////////////////////////////////////////////////////////////////////////////////////
float CalibrationEC= (Calibration_PPM*2)/1000;
float Temperature;
float EC;
float EC_at_25;
int ppm;
float A_to_D= 0;
float Vin= 5;
float Vdrop= 0;
float R_Water;
float Value=0;
//Leave the next 2 lines in if you need help later on///////////////////////////////////
//Ask any questions that you may have in the comment section of this video
//https://youtu.be/-xKIczj9rVA

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

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,15);
  display.println("Electrolyte Meter");
  display.display();
  delay(3000);
  pinMode(EC_Read,INPUT);
  pinMode(ECPower,OUTPUT);

//////////////////////////////////////////////////////////////////////////////////////////
// Calibrate (); // After calibration put two forward slashes before this line of code
//////////////////////////////////////////////////////////////////////////////////////////
}
void loop() {
  GetEC(); //Calls GetEC()
  delay(6000); //Do not make this less than 6 sec (6000)
}

void printOLED () {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
}
////////////////////////////////////////////////////////////////////////////////////
void GetEC() {
  int val;
  double Temp;

  val = analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature = Temp_C;

  digitalWrite(ECPower,HIGH);
  A_to_D= analogRead(EC_Read);
  A_to_D= analogRead(EC_Read);
  digitalWrite(ECPower,LOW);
  Vdrop= (Vin*A_to_D) / 1024.0;
  R_Water = (Vdrop*R1) / (Vin-Vdrop);
  EC = 1000/ (R_Water*K);
  EC_at_25 = EC / (1+ Temp_Coef*(Temperature-25.0));
  ppm=(EC_at_25)*(PPM_Con*1000);

  printOLED ();
  display.setCursor(0,15);
  display.println("EC: ");
  display.print(EC_at_25);
  display.print("mS/cm");
  display.display();
  delay(3000);

  printOLED ();
  display.setCursor(0,15);
  display.println("TDS: ");
  display.print(ppm);
  display.print(" ppm");
  display.display();
  delay(3000);

  printOLED ();
  display.setCursor(0,15);
  display.println("Temp: ");
  display.print(Temperature);
  display.print("C");
  display.display();
  delay(3000);

  // Serial.print(" EC: ");
  // Serial.print(EC_at_25);
  // Serial.print(" milliSiemens(mS/cm) ");
  // Serial.print(ppm);
  // Serial.print(" ppm ");
  // Serial.print(Temperature);
  // Serial.println(" *C ");
}
////////////////////////////////////////////////////////////////////////////////////
void Calibrate () {
  Serial.println("Calibration routine started");

  float Temperature_end=0;
  float Temperature_begin=0;
  int val;
  double Temp;

  val=analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature_begin = Temp_C;
  Value = 0;
  int i=1;
  while(i<=10){
    digitalWrite(ECPower,HIGH);
    A_to_D= analogRead(EC_Read);
    A_to_D= analogRead(EC_Read);
    digitalWrite(ECPower,LOW);
    Value=Value+A_to_D;
    i++;
    delay(6000);
  };
  A_to_D = (Value/10);
  val = analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature_end=Temp_C;
  EC =CalibrationEC*(1+(Temp_Coef*(Temperature_end-25.0)));
  Vdrop= (((Vin)*(A_to_D))/1024.0);
  R_Water=(Vdrop*R1)/(Vin-Vdrop);

  float K_cal= 1000/(R_Water*EC);

  Serial.print("Replace K value with K = ");
  Serial.println(K_cal);
  Serial.print("Temperature difference start to end were = ");
  Temp_C=Temperature_end-Temperature_begin;
  Serial.print(Temp_C);
  Serial.println("*C");
  Serial.println("Temperature difference start to end must be smaller than 0.15*C");
  Serial.println("");
  Calibrate ();
}
////////////////////////////////////////////////////////////////////////////////////

Final Test: Switched from 5V to 3.3V supply voltage (Success..!?)

After countless of trials and errors to debug and figure out what’s the problem behind this negative result, it finally shows the correct reading once I switched the supply to 3.3V. I must admit that I had to go to chatgpt to consult and help me debug what’s the problem, because I initially thought it was the calculation and code problem.

Later I found out that there is limitation on the XIAO RP2040 regarding the voltage input of its I/O pins. In the Seeed’s page, it’s mentioned there that for general I/O pins: Working voltage of MCU is 3.3V. Voltage input connected to general I/O pins may cause chip damage if it’ higher than 3.3V.

Furthermore, this page explains about the Steinhart-Hart calculation for the NTC and concludes in the end that for accurate measurements it is better to use the 3.3V line as analog reference and feed the resistor from there.

Reflection

This assignment has reinforced me the importance of thoroughly understanding and mastering the devices you use, particularly their power requirements and operating voltages. It’s crucial to prevent any potential damage as well as to get the expected readings to our components or devices.

Aside from that, during testing and prototyping, I used many jumper cables, with the help of breadboard. I realized that most of the time, the problems came from poor-quality cables or incorrect wiring connections. So, most of the issues were usually due to physical properties rather than the code.

Other Input Devices

Aside from the NTC I also explore other input devices such as the TMP36 and Potentiometer. But I will get back to this later since I will prioritize finishing my other documentations first…

TMP36 Temperature Sensor

Components

1-temp-sensor 2-board

  • Temperature Sensor TMP36
  • Microcontroller Dev Board -with Xiao RP2040 MCU

Wiring

3-wiring

Code

12-4-large

int celsius = 0;
int fahrenheit = 0;

void setup()
{
  pinMode(A1, INPUT);
  Serial.begin(9600);
}

void loop()
{
  // measure temperature in Celsius
  celsius = map(((analogRead(A0) - 20) * 3.04), 0, 1023, -40, 125);
  // convert to Fahrenheit
  fahrenheit = ((celsius * 9) / 5 + 32);
  Serial.print("Temperature: ");
  Serial.print(celsius);
  Serial.print(" C, ");
  Serial.print(fahrenheit);
  Serial.println(" F");
  delay(1000); // Wait for 1000 millisecond(s)
}

Potentiometer

Potentiometer as input device and servi as output device

9-6

#include <Servo.h>

Servo tafServo;

int val;

void setup() // 
{
  tafServo.attach(D3);
}

void loop()
{
  val = analogRead(A2); 
  val = map(val,0,1023,0,180); // map 10 bit value to 8bit value
  tafServo.write(val);
  delay(15);
}