Calculating the Average Temperature of Multiple NTC Thermistors in series?

Hi all. I am trying to get the average room temperature of a 'larger' space using four to five NTC 20k 3950B Thermistors. I looked at some examples on how to wire and program it up to an Arduino Uno, interpreting some code and examples online. Once I had one thermistor hooked up and accurately reading temps, I cleaned up the code a little bit and moved onward to hooking up multiple in a series.

I figured that if I soldered together four thermistors in a series, the total nominal resistance could just be multiplied by the quantity of probes (in this case 4). So in my example, if I am using four 20k thermistors, then I should be able to change my values to 20,000 * 4 == 80,000. The B value (3950) should stay the same, since the probes are all of the same type and have identical curves in proportion to temperature. The only other thing I thought could potentially change would be the nominal temperature, which is typically the resistance at a given room temperature (typically 25C).

Anyway, I tried multiplying these resistance values by a factor of 4 and my temperature values were pretty far off. I had my values being reported over the Serial port for debugging. When I take the probes out of the voltage divider circuit and measure them in a series at around room temperature using a multi-meter, I was getting about 90k which sounds in the right ballpark. This what also showed up on the Serial monitor when printing as well, so it sounds like the calculation and math from the original voltage divider circuit translates OK.

I am using a single 20k ohm resistor as the 'static' resistor on the voltage divider circuit in order to calculate the unknown thermistor resistance by measuring the analog voltage on the ADC of the Arduino. This calculated ADC value also checks out on the multimeter, so I believe that eliminates that as a potential issue too.

The only thing I could not easily understand through some of the online examples/tutorials were the steinhart equations. These were what translate the resistance values into a identifiable temperature. I found the online explanation to be lackluster and the code a bit sloppily presented, so I just copy/pasted that part and integrated it into my code.

Although I had it working perfectly before with a single Thermistor, I am fairly certain that there is something that needs to be tweaked in these calculations to get four to work in a series together. I also thought perhaps there may be some other hardware based thing I might be overlooking, since I am still mostly a novice with electrical components.

Any help would be appreciated. I'll leave my chicken scratch code here for you to look over.

// thermistor-1.ino Simple test program for a thermistor for Adafruit Learning System
// https://learn.adafruit.com/thermistor/using-a-thermistor by Limor Fried, Adafruit Industries
// MIT License - please keep attribution and consider buying parts from Adafruit

bool debugging = true;

/*
 *  ____________HARDWARE ENVIROMENT PERAMETERS______________
 * 
 */

const int numAnalogPins = 5; // Number of physical connections to enable.
int pinLogicTable[5] = {A0, A1, A2, A3, A4}; // Array to index logic pins. 
float pinSupplyVoltage[numAnalogPins];
float ViTable[numAnalogPins]; // Array to store the calculated input voltages for each channel.
float VoTable[numAnalogPins]; // Array to store the calculated output voltages for each channel.
float R1Table[numAnalogPins]; // Array to store the calculated resistance of each channel.
float tempTable[numAnalogPins]; // Array to store the calculated resistance of each probe (C).


/* _____________THERMISTOR PERAMETERS______________
 *  
 */

// Pin I/O for reading temperatures. (Deprecated).
int THERMISTOR_PIN_1 = A0;
int THERMISTOR_PIN_2 = A1;
int THERMISTOR_PIN_3 = A2;
int THERMISTOR_PIN_4 = A3;
int THERMISTOR_PIN_5 = A4;
         
// Thermistor Resistance Rating (at 25C).
int THERMISTOR_NOMINAL = 20000 * 4;      

// The beta Coefficient of the thermistor (usually 3000-4000)
int B_COEFFICIENT = 3950;

// Secondary resistor value (in ohms). Used as a voltage divider to calculate the change in the Thermistors resistance.
int SERIES_RESISTOR = 20000;

// The value of the power supply voltage
float SUPPLY_VCC = 5.0;

// Temperatures for nominal resistance (This is almost always 25 C)
int TEMPERATURE_NOMINAL = 25;   

// Number of times to resample. More samples means better accuracy at the cost of latency.
int NUM_SAMPLES = 16;
 
void setup(void) {
  Serial.begin(9600);
}
 
void loop(void) {
  
   // Read all voltages on the ADC.
  for (int i = 0; i < numAnalogPins; ++i){
  
     // SAMPLE SMOOTHING:
     float sampleAverage = 0; // Store average resistance value.
    
     // Resample analog port 'x' times for better accuracy.
     for (int j = 0; j < NUM_SAMPLES; ++j) { 
      sampleAverage += analogRead(pinLogicTable[i]);
     }
     
     // Find average from 'x' amount of samples.
     sampleAverage /= NUM_SAMPLES; 

     // If resampling is > 1, it will save the AVERAGE from all samples.
     ViTable[i] = sampleAverage;
   
  }

  // Find the output Voltage (from the voltage divider circuit).
  for (int i = 0; i < numAnalogPins; ++i){
    VoTable[i] = (SUPPLY_VCC / 1023) * ViTable[i];
  }

  // Find the unknown resistance value of the Thermistor, using a Series resistor of known value.
  for (int i = 0; i < numAnalogPins; ++i){ 
    R1Table[i] = ( (SUPPLY_VCC * SERIES_RESISTOR) - (SERIES_RESISTOR * VoTable[i]) ) / VoTable[i];    
  }

  // Convert the resistance to Temperature (C).
  for (int i = 0; i < numAnalogPins; ++i) {
    float steinhart;  
    steinhart = R1Table[i] / THERMISTOR_NOMINAL;                                // (R/Ro)
    steinhart = log(steinhart);                                         // ln(R/Ro)
    steinhart /= B_COEFFICIENT;                                         // 1/B * ln(R/Ro)
    steinhart += 1.0 / (TEMPERATURE_NOMINAL + 273.15); // + (1/To)
    steinhart = 1.0 / steinhart;                                        // Invert
    steinhart -= 273.15;                                                // convert to C

    tempTable[i] = steinhart;
  }

  // Report debugging information over serial.
  if(debugging){
    
    for (int i = 0; i < numAnalogPins; ++i){
      Serial.print("Pin Logical Address: ");
      Serial.print(pinLogicTable[i]);
      Serial.print(" Supply Voltage = ");
      Serial.print(SUPPLY_VCC);
      Serial.print(" PWM In: "); 
      Serial.print(ViTable[i]);
      Serial.print(" Voltage Out: ");
      Serial.print(VoTable[i]);
      Serial.print(" Thermister Resistance: ");
      Serial.print(R1Table[i]);
      Serial.print(" Probe Temperature (C): ");
      Serial.print(tempTable[i]);
      Serial.println();
      }

      Serial.println();
    }

  delay(2000);
}

Serial monitor:

Pin Logical Address: 14 Supply Voltage = 5.00 PWM In: 171.88 Voltage Out: 0.84 Thermister Resistance: 99040.00 Probe Temperature (C): -12.81

int THERMISTOR_NOMINAL = 20000 * 4; 80000 doesn't fit in an int if an int is sixteen bits

TheMemberFormerlyKnownAsAWOL:

int THERMISTOR_NOMINAL = 20000 * 4;

80000 doesn't fit in an int if an int is sixteen bits

This was indeed one issue I overlooked. However, after changing it to a long int I was still encountering incorrect numbers. To troubleshoot, I tried casting data types to long and doubles during calculations but to not avail. Today, I found the source of the issue, which was that the multiplication was not taking place before the variable assignment. Specifically:

// Thermistor Resistance Rating (at 25C).
long int THERMISTOR_NOMINAL = (20000 * 4);

If I changed this value to be 80000 instead, it would work without problems. However, it does not seem the compiler will do the math before the assignment. This is very strange to me, as I've worked mostly in C++ and python in the past and I've never encountered something like this before.

Does anyone know a reason as to why this is happening? Is this a character/behavior seen in C, but not C++? I assume that the Arduino IDE uses a fairly up to date version of the GCC compiler.

void setup()
{
    Serial.begin(115200);
    long int THERMISTOR_NOMINAL = (20000 * 4); 
    Serial.println(THERMISTOR_NOMINAL);
}

void loop()
{
}

Generates a warning if you have compile warnings enabled in File, Preferences.

C:\...\arduino_modified_sketch_752939\sketch_oct17c.ino:4:42: warning: integer overflow in expression [-Woverflow]
     long int THERMISTOR_NOMINAL = (20000 * 4);
                                    ~~~~~~^~~

Since both 20000 and 4 will fit in an int, the math is done and the answer is returned as int.

void setup()
{
    Serial.begin(115200);
    long int THERMISTOR_NOMINAL = (20000 * 4L); 
    Serial.println(THERMISTOR_NOMINAL);
}

void loop()
{
}

Adding the L to the 4 (the L casts the 4 to long data type) forces the math to be done with the long data type. No warning and correct answer.

I am trying to get the average room temperature

The conversion from thermistor resistance to temperature is very nonlinear, so the result using four thermistors in series will not be equal to the average of the results from individual thermistors. In other words, with probes in series, you are not calculating an average temperature.

The larger the variations in temperature at the individual probes, the larger the discrepancy.

Hi,

I figured that if I soldered together four thermistors in a series, the total nominal resistance could just be multiplied by the quantity of probes (in this case 4).

Why?
Isn't it easier to use 4 analog inputs and measure each thermistor accurately, then calculate the average.

The thermistor response to temperature is not linear, that is why you have elaborate equations to do the conversion.

You are assuming a linear relationship.
If it was then using a linear averaging algorithm like you are would work.

Calculate each individual temperature then average them.

Tom... :slight_smile:

groundFungus:

void setup()

{
   Serial.begin(115200);
   long int THERMISTOR_NOMINAL = (20000 * 4);
   Serial.println(THERMISTOR_NOMINAL);
}

void loop()
{
}



Generates a warning if you have compile warnings enabled in File, Preferences.


C:...\arduino_modified_sketch_752939\sketch_oct17c.ino:4:42: warning: integer overflow in expression [-Woverflow]
    long int THERMISTOR_NOMINAL = (20000 * 4);
                                   ~~~~^



Since both 20000 and 4 will fit in an int, the math is done and the answer is returned as int.



void setup()
{
   Serial.begin(115200);
   long int THERMISTOR_NOMINAL = (20000 * 4L);
   Serial.println(THERMISTOR_NOMINAL);
}

void loop()
{
}



Adding the L to the 4 (the L casts the 4 to long data type) forces the math to be done with the long data type. No warning and correct answer.

Just to clarify so I understand correctly: When doing the math before the assignment, it assumes the data type of the constant values are of an int type, so the final result is limited by an the bounds of a basic int unit before it is ever stored into the new long int variable. Yes?

Yes. That is the way that I understand it.

TomGeorge:
Hi,
Why?
Isn't it easier to use 4 analog inputs and measure each thermistor accurately, then calculate the average.

The thermistor response to temperature is not linear, that is why you have elaborate equations to do the conversion.

You are assuming a linear relationship.
If it was then using a linear averaging algorithm like you are would work.

Calculate each individual temperature then average them.

Tom... :slight_smile:

Hi Tom.

The reason for not doing them individually on each ADC channel then finding the average after, is I wanted to leave those ports open so I can control other devices (such as fans) using a single board instead of using many boards.

Since the application at hand is within a 3D printer chamber, I also need to control the speed of an individual fan on each stepper motors so that they do not burn-out inside the heated chamber. I don't need to have them on 100% when printing plastics that require less ambient heat, so I wanted to throw a NTC probe on each motor to monitor and modulate the speeds as necessary. If there are 3 motors needing cooling, the I/O required for both the motor and chamber probes would fill up a singular Arduino in of itself. However, if I can consolidate the 4 NTC 'chamber' probes onto a single ADC channel, then I can make it all work with a single board.

I also plan to use a medium-sized 3000 RPM A/C motor pulled out of a duct-fan to act as a convection fan inside the heated chamber. I planned to control it's speed based on the temperature data reported by the NTC series probes. To do this, I would be using a light-dimming/current chopping module I found through robotdyn. I do not believe it needs an pin from the ADC to control the speed, but regardless, it would be nice to save I/O whenever possible. Who knows if I'll need any other ports for temp monitoring and fans down the road.

A question to you however, is why would the average temperature of the probes be inaccurate? I understand the value of resistance to temperature is non-linear, which I believe is what the Beta value represents. But I don't believe the Beta value would change if the probes are all of the same type, because they all share the same resistance/temperature relationship right?

And if the Beta value does change when thermistors are installed in a series, is there any way to calculate the new beta value given the new setup?

I have been perusing around HVAC forums and done a few google searches while trying to troubleshoot this, and it does not seem uncommon for people to use NTC Thermistors in series just like this. Was this an oversight on their end or is there something they are doing which they might not have mentioned?

Hi,

I also need to control the speed of an individual fan on each stepper motors so that they do not burn-out inside the heated chamber.

If you know the average temperature, how will you know which fan is overheating?

I am surprised you are not using an ADC module with 4 inputs, I2C comms.

google arduino adc module

Tom... :slight_smile:

What is the range of temperatures that you need to measure?

The reason for not doing them individually on each ADC channel then finding the average after, is I wanted to leave those ports open

If you are fortunate, this won't be an expensive learning experience.

TomGeorge:
Hi,
If you know the average temperature, how will you know which fan is overheating?

I am surprised you are not using an ADC module with 4 inputs, I2C comms.

google arduino adc module

https://core-electronics.com.au/gravity-i2c-ads1115-16-bit-adc-module-arduino-raspberry-pi-compatible.html

Tom... :slight_smile:

Sorry if I did not explain well. I am going to have a thermistor on each motor and 4 probes for the chamber. If I could run the 4 probes on a single ADC channel, I could get away with not buying expansion boards and use a $1.50 arduino uno for the whole thing.

Temperature ranges would be around 0-120 C

Have you considered the DS18B20 one wire digital temperature sensors. Several sensors can be on a one wire bus (one processor pin) and can be read individually. They have a measurement range of -55 to 125°C. They read directly, no equation to solve.

The DallasTemperature library and the OneWire library make using them pretty easy. Both libraries are available via the library manager.

So yes, while I thought of going that route and getting a singular, more accurate sensor, I figured it would not be as effective as 3-4 sensors at different heights throughout the enclosure.

The reason for this, is I have a fairly large printer. It's volume is approx 24x24". With classic cartersian style 3D printers, you don't know what height the build plate is going to be since it changes during the print. So I wanted get the best average temperature in the enclosure by measuring multiple points of height within the enclosure.

If I had instead bought one of the temperature sensors you recommended, I'd likely have to mount it somewhere near the top of the enclosure (since heat rises) and it might not aid the lower layer levels during the print.

I figured the cheapest and best way to go about this, would be the NTC type thermistors and a single arduino uno. Pack of thermistors is $5. An uno is $1.50 on ebay.

Hi,
I think you may have missed the point about the DS sensors.

You still use 4 spaced around the enclosure.

They ALL share the same signal wire as each has a unique address.
There is a library and examples showing you how to accomplish this.

If you use these ones.
Waterproof and with leads.

Tom... :slight_smile:

So yes, while I thought of going that route and getting a singular, more accurate sensor, I figured it would not be as effective as 3-4 sensors at different heights throughout the enclosure.

That is what you can do with the DS18B20. You could have 4 or 8 sensors at different places in the enclosure and be able to read them individually. And use just 1 pin for all of them (one wire BUS).

If I had instead bought one of the temperature sensors you recommended, I'd likely have to mount it somewhere near the top of the enclosure (since heat rises) and it might not aid the lower layer levels during the print.

That is what I am trying to point out. You would not use just one sensor. You could have many sensors and not use any more pins for them than you are using now.

I figured the cheapest and best way to go about this, would be the NTC type thermistors and a single arduino uno. Pack of thermistors is $5. An uno is $1.50 on ebay.

Here are 10 sensors for $10.00. $1.00 each.

ooooooo! I did not know they came this cheap. I just did not want to pay $20-30 for a handful of sensors...

It is good to know that I can run these off a single bus. However, at the moment since I already purchased the 20 NTC thermistor probes ($5), I might just run them all on a dedicated ADC and use 2 arduino nano boards seperately. I ordered like, 10 nano's on eBay for $15 just so I can have spares lying around for projects.

So, for now, I'll split off the duty's onto different boards I guess:

1 board will monitor and control the motor temperatures and fan speeds using the NTC thermistors individually.

1 board will monitor the enclosure temperature and the convection fan speeds.

However, If I were repurchasing hardware again I would probably go with those bus sensors mentioned earlier. I did not know they could be purchased for $1 a piece. This would allow me to go with 1 arduino board and control everything.

I don't regret going with the NTC thermistors though, since I needed them anyway to monitor the motor temperatures.