Being able to measure the temperature at a single point using a thermistor is great, but often we will need more than just one in a practical system. For example, an over-temperature protection and fan-speed control system may need to evaluate the temperature at several separate heatsinks within the same device. How can we do this and what are the challenges?
In this blog, I also take a break from the precision measurements made using high-resolution DMMs, instead implementing something practical that can make readings from multiple thermistors.
Table of Contents
What’s the Problem?
When we use digital temperature or environmental sensors, it’s a simple case of connecting them to power, a digital bus and configuring a unique address. There is very little else to worry about, purely because it takes care of all of the analog hassles within the sensor package itself.
But thermistors are inherently analog devices. They vary their resistance with temperature in a non-linear way and the most simple form of interfacing relies on a divider resistor which needs to be stable and have an accurately known value to be able to derive accurate resistance measurements. The temperature is then derived by an approximation, curve function or look-up table using the resistance value.
This means we are dealing with analog voltages, which are vulnerable to introduced errors and noise. One must, hence, tread carefully.
ADC Resolution, Voltage Range, Reference Voltage
While I do have quite a few ESP8266-based boards from previous element14 wins, I’ve decided not to use them. One issue is that there is just a single ADC which requires some creativity to work around, but another is the 10-bit ADC which is not ideal for having higher temperature resolution. Similarly, I have decided not to use AVR-based Arduinos either, because they too have 10-bit ADCs which is not ideal. Such ADCs can only resolve the voltage range to 1024 discrete steps (including zero, assuming unipolar), thus giving a very limited temperature resolution. Higher-resolution ADCs are ideal, but one must also be careful to understand how many of those bits are effective (as there are going to be non-linearity errors and noise. In the case of ESP8266, some users have measured effective number of bits between 6 to 8, especially when Wi-Fi transmissions are occurring, while AVR chips frequently have 8 to 9 bits effective.
Oversampling can help, but as mentioned before by michaelkellett, it is not as simple as averaging readings if one wants to achieve the full theoretical number of bits of resolution improvement. Without any dithering noise or inconsistency in ADC conversions, oversampling would provide no benefit at all. As mentioned before, oversampling trades off sample rate for reduced noise and potentially increased resolution.
An additional wrinkle comes about with the ESP8266 is the internal ADC reference voltage appears to be 1.0V. This means that the input voltage needs to be in the range of 0-1V, but the 1V reference source is not available externally, so any thermistor voltage divider would be using a separate voltage reference which can introduce errors. Furthermore, it would usually necessitate an additional voltage divider attached to the input signal (e.g. to scale a 3.3V voltage divider down to 1V) which is likely to cause a reduction in input impedance resulting in additional voltage errors. The use of an opamp for scaling would reduce these errors.
Using the Vcc voltage as reference, as is default with most Arduinos, would be beneficial from this perspective as it requires no additional scaling, but the voltage rail would not necessarily be clean because of the potential presence of digital noise which can propagate into the readings. Adding capacitance can help to smooth out the noise.
Multiplexing Considerations – Mechanically or Electrically?
The need to measure multiple analog signals accurately has been a key requirement in various research and industrial scenarios. As a result, multiplexed data acquisition systems have existed for many years, some of which were simply made by using a standard DMM interfaced to a scanner.
More recently, there have been integrated data acquisition mainframes such as the Keysight DAQ970A which was previously RoadTested (although I missed out on it) which consists of a DMM and a number of slots where switching modules can be installed. While I don’t have one of these, I do like the fact that the datasheet gives us nice diagrams indicating just how these modules work -
The general-purpose module is organised where a set of channel switches isolates both lines from a bus. The Bank Switch allows for routing each group of ten inputs to either the measurement or sense inputs in concert with the Backplane Switches. The Backplane Switches also allows isolating the module from the bus entirely, so other modules can access the DMM. It’s basically simulating someone plugging and unplugging inputs, but it can do it quickly and automatically!
There are other cards of different designs – a single-side switching multiplex card is also available, which would be enough for thermistor use especially when one-side is common amongst all thermistors.
There are also solid-state solutions which have been used in process recorders, such as the B&K Precision/Sefram DAS240-BAT which I have previously reviewed which uses OptoMOS relays instead.
Each of these options have advantages and disadvantages.
The Mechanical Option
A relatively old-technology for multiplexing is to simply use relays, which are electrically-controlled switches. Historical telephone exchanges, railway signalling systems and early computers were built arrays of such relays. As they are essentially switches, they mechanically connect and disconnect the signal, keeping a high-level of isolation and being relatively agnostic to the signal passing through (as long as it is within the operating range), being key advantages.
The use of relays has been less common in modern electronics for a number of reasons. The electromechanical nature of relays means that they do have both a mechanical and electrical cycle lifetime due to the erosion of contact material causing changes in contact resistance and mechanical fatigue. Relays also require a fair amount of power to operate and keep latched (in the case of non-latching types), with their inductive coils also needing care to avoid damage to driving circuitry due to back-EMF inductive spikes. Switching also causes acoustic noise and relays can be vulnerable to mechanical shock and vibration. Finally, high-quality relays can be fairly expensive because of the use of precious metals in the contacts. For many applications, an electrical approach to switching is now preferable because of these disadvantages.
However, in some precision applications (e.g. in test and measurement equipment) and in high-power applications, it can be difficult to make sufficiently performant solid-state implementations, so relays are still fairly common.
It is important to understand that choosing the right relay is very important for the application. General purpose power relays are optimised for making and breaking larger current flows, resulting in platings which are resistant to pitting. These platings may not be ideal for signal applications as they may form oxides and a minimum current/voltage is necessary to “clean” and seal the contact. Signal relays are often produced with softer, precious-metal contact plating which would only be able to switch limited currents but have more consistent contact resistance when used with very small currents involved in switching signals. Misusing these relays at higher currents will likely destroy the coating and make the relay less reliable. Different styles of relay are available which have an effect on switching speed (e.g. reeds switch faster than ice-cubes), how switching occurs (latching/non-latching) and the types of signals that can be switched (low-frequency/RF).
In my own relay multiplexer design, I’ve used Nexem (NEC-Tokin) EA2-5NU 5V DPDT relays which are driven by a custom-design Arduino shield. Such a relay has gold-plated silver alloy contacts with a 10mVDC/10µA minimum contact rating suitable for signal use but with a 1A maximum switching current rating which makes it durable to minor mishaps. The dual-pole nature allows for switching both legs of the connection, minimising leakage and making it more versatile for other uses, however in the case of thermistor usage this is not really necessary. I managed to use it in the Keysight Smart Bench Essentials review with thermistors and it seemed to work fairly well. But alas, I think the mechanical solution is best reserved for precision uses only due to cost, noise, power consumption and lifetime concerns.
The Electrical Option
Moving to electrical solid-state switching has the potential to overcome many of the disadvantages of electromechanical switching. As it has no moving parts, they can switch quickly, silently and without wearing out. They are often also more compact and allows for denser designs. Price-wise, some simpler solutions can be lower-cost, but this is not necessarily the case for high-performance parts.
Commonly, MOSFETs may be used for switching, but they do some with some disadvantages. This can include fragility in vulnerability to over-voltage, over-current, over-temperature and electrostatic discharge. They also do not “switch off” perfectly, often having a small leakage current and their “on-state” has a residual resistance which may be greater than that of relay-based contacts. Using MOSFETs directly also have limited isolation between the switched signal and the gate drive – merely a thin layer of oxide separates the two which may not be enough to keep equipment or users safe in case of a fault. Generating the necessary gate-drive voltages may require additional circuitry in some cases.
To address the issues with isolation, there are higher-end OptoMOS devices (such as those from IXYS) which are optically-triggered solid-state relays which can be suitable for signal applications. Using them is almost as simple as turning on an LED. They are not all that expensive for low-current applications, but they are perhaps not common enough to be in everyone’s junk-box just yet. I know I don’t have any of them on-hand!
Another option is just to use a dedicated multiplexer IC. The CD4051B is a very popular, old-fashioned, 8-input analog multiplexer that can be used, although more modern alternatives are probably available and preferable.
You could theoretically interface eight thermistors to a single ADC input, using three selection bits to choose which input is used. You could theoretically common these three selection bits between a few of these, using the inhibit (INH) line to select which “bank” of eight inputs (i.e. multiplex chip) is active. This is an all-solid-state chip which is also available in DIP form factor for easy breadboarding, although it usually performs best with 5V and above so logic level conversion may be necessary, although I have gotten away with 3.3V at the cost of increased channel resistance.
Such a solution may seem alluring, and it will probably do the job based on previous experience, but with some key caveats. The first is that the multiplex IC adds a noticeable amount of resistance to the input – this could be anywhere from hundreds of ohms to a kilo-ohm depending on the supply voltage to the IC. For a high-impedance input to a DMM, this may not be such an issue, but switched capacitor ADCs would need to draw some current and this could mean reduced accuracy (under-reading) or slower settling times which limits your switching speed (less of an issue with thermistors which are generally not read quickly anyway due to their own intrinsic response time). Another issue could be current leakage, which is usually specified in the hundreds of nA-range, which may cause some offsets to occur. Compared to the actual current through the thermistors which would be at least in the µA range, this is perhaps still not a big issue. I have previously used such circuits to multiplex force-sensitive resistors (FSRs) which are similarly resistance-based sensors without significant issues … so I suspect I won’t be reimplementing this one (at least, not in this blog).
No Multiplexing at All?
But let’s say you don’t want to bother with any of that and still want to host multiple thermistors in a single design. What can you do?
One solution would be to use multiple ADCs, one connected to each thermistor. The ADC would interface to the microcontroller over a digital bus of some sort (commonly I2C or SPI), thus transforming the sensor into a “digital” one. Common examples include the ADS1115/ADS118 16-bit ADC modules which commonly retail around AU$28.
By using an “external” ADC that is not integrated into a microcontroller, it is possible to chose higher-resolution, better-quality ADCs and have lower noise due to not being integrated into the same package. While high-end ADCs are available, with increasing resolution comes more stringent design requirements to achieve the full resolution advertised. It is possible to combine a higher-end external ADC with a multiplexing solution so as to get multiple channels and high resolution regardless of the microcontroller used. The main downside is the cost of such a solution, and the availability of parts, especially in the present-day chip shortage.
Another easy-way-out would be to pick a microcontroller that has more flexibility than the ESP8266 in its input pins such that there can be multiple analog inputs. This would rely on the internal input matrix of the microcontroller to perform the multiplexing needed, but is perhaps the easiest solution. Arduino SAMD21-based boards, for example, have seven analog inputs across a single 12-bit ADC, while ESP32 boards apparently can use 18 separate channels across two hardware 12-bit ADCs!
Experiment: Practically Running Multiple Thermistors
So, how did I go about practically running multiple thermistors? First, I chose a platform – unfortunately, as I don’t have any spare ESP32 boards on-hand, I settled on re-using my Arduino MKR WiFi 1010 board that I received as part of the Omron Environmental Sensors RoadTest. As the board natively has Wi-Fi, this allows for me to connect it to my home network and pass results as UDP packets to my single-board computers for logging. This way, I can place the experiment into more thermally-interesting locations.
This is a SAMD21-based board with seven analog inputs and 12-bit resolution, although it is also known that analogRead is not particularly fast, taking about 435µs for a reading by default (~2.3kHz sample rate). It is also known to be somewhat noisy as a switched-capacitor successive approximation ADC and would be best driven by an opamp-buffered low-impedance input. I, however, decided to omit this as I didn’t have seven nice op-amps to spare and it would make more sense to use one op-amp and a multiplex circuit instead which is something I was trying to avoid.
The ADC is also known to be somewhat non-linear, potentially benefiting from a full-scale and zero offset calibration, but I also did not do this. Instead, I took the understanding (based on the work linked above) that averaging does improve linearity. I decided to use AVDWeb’s implementation of analogReadFast to allow me to get enough speed to take averaging to the extreme. Their testing showed that the standard deviation of samples went down continually up to 32-samples, although with diminishing returns. I decided to do the average of 4096-samples instead, to get another (theoretical) 12-bits. After all, it shouldn’t make things any worse, but it may not make things any better. But instead of doing the division on the microcontroller, I decided to add all 4096-samples together into a 32-bit int to be passed off to the computer for later division as a 24-bit value.
The choice of divider resistors was made to match each thermistor’s nominal 25°C value, as I will be measuring ambient temperatures with this set-up and room temperature is around that value. Of course, no resistor is perfect, and my stash of resistors comes from different vendors and are mostly low-precision carbon film type resistors which may have quite an offset and thermally-induced drift too. I had no 5kΩ resistor, so I settled for 5.1kΩ instead. Regardless, I did make an initial spot-measurement with the Keithley 2110 5.5-digit DMM to use as a reference value:
- 3.0061k (+0.20%)
- 4.6505k (-1.05%)
- 5.0852k (-0.29%)
- 9.9478k (-0.52%)
- 11.928k (-0.60%)
- 29.610k (-1.30%)
- 47.374k (+0.80%)
While the resistances are mainly 5% tolerance by marking, the resulting errors were often within 1%, with the occasional value just over 1%. This seems to be a fair match given the thermistors themselves have a 1% tolerance value.
I did make a concession to try and improve the voltage rail stability so that there is less noise creeping into the measurement. To do this, I shoved two 100nF greencap polyester capacitors across the voltage rails. Better than nothing, perhaps, which is what I was originally thinking of doing.
For a temperature measurement comparison, I decided to enlist some spare Bosch BMP280 digital environmental sensors which I had left behind from previous eBay disappointments. These are not highly-accurate, being rated at around ±1°C, so I decided to put two of them in the design so we can at least have a second opinion. This is easily possible by jumpering the address bit so that the two do not have the same address. If I wanted more, I’d have to use an I2C multiplexer like the TCA9548A which would add additional complication.
The physical construction was achieved on a breadboard, with (somewhat messy) wiring. Nevertheless, it is sufficient for the purposes of this experiment.
I let the thermistors rise freely from the board, to reduce board-heating effects, but also decided to bundle them together so that the beads are in close proximity to reduce temperature differentials between the thermistors.
The Arduino sketch code is as follows:
// element14 Experimenting with Thermistors Design Challenge // Seven Thermistors + Two BMP280 Sensors via Wi-Fi UDP // by Gough Lui (goughlui.com) - Aug 2022 // No warranties provided or implied. // Intended for use with Arduino MKR WiFi 1010 // Requires AnalogReadFast from https://www.avdweb.nl/arduino/adc-dac/fast-10-bit-adc // Requires Adafruit BMP280 from https://github.com/adafruit/Adafruit_BMP280_Library // Edit Wi-Fi details, source port, destination address, destination port for your needs #include <Wire.h> #include <WiFiNINA.h> #include <WiFiUdp.h> #include "avdweb_AnalogReadFast.h" #include <Adafruit_BMP280.h> int status = WL_IDLE_STATUS; char ssid[] = "INSERT_YOUR_SSID_HERE"; char pass[] = "INSERT_YOUR_PASS_HERE"; unsigned int localPort = 19999; // Source Port char packetBuffer[512]; WiFiUDP Udp; unsigned int thermistorvals[7]; const byte pins[] = {A0,A1,A2,A3,A4,A5,A6}; Adafruit_BMP280 bmp; Adafruit_BMP280 bmp2; char bmptemp1[12]; char bmptemp2[12]; void setup() { //Serial.begin(115200); //while(!Serial) { //} // Wait for Serial Connection analogReadResolution(12); analogRead(A0); // First read is bad if (WiFi.status() == WL_NO_MODULE) { //Serial.println("Communication with WiFi module failed!"); while (true); } String fv = WiFi.firmwareVersion(); if (fv < WIFI_FIRMWARE_LATEST_VERSION) { //Serial.println("Please upgrade the firmware"); } while (status != WL_CONNECTED) { //Serial.print("Attempting to connect to SSID: "); //Serial.println(ssid); status = WiFi.begin(ssid, pass); delay(10000); } //Serial.println("Connected to wifi"); //Serial.print("SSID: "); //Serial.println(WiFi.SSID()); // print your board's IP address: //IPAddress ip = WiFi.localIP(); //Serial.print("IP Address: "); //Serial.println(ip); // print the received signal strength: //long rssi = WiFi.RSSI(); //Serial.print("signal strength (RSSI):"); //Serial.print(rssi); //Serial.println(" dBm"); //Serial.println("\nStarting connection to server..."); Udp.begin(localPort); while (!bmp.begin()) { //Serial.println("Could not find BMP280 at default address!"); delay(5000); } bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ Adafruit_BMP280::SAMPLING_X16, /* Temp. oversampling */ Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */ Adafruit_BMP280::FILTER_X16, /* Filtering. */ Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ while (!bmp2.begin(BMP280_ADDRESS_ALT)) { //Serial.println("Could not find BMP280 at secondary address!"); delay(5000); } bmp2.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ Adafruit_BMP280::SAMPLING_X16, /* Temp. oversampling */ Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */ Adafruit_BMP280::FILTER_X16, /* Filtering. */ Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ } void loop() { for (int i=0;i<7;i++) { thermistorvals[i]=0; } for (int i=0;i<7;i++) { for (int j=0;j<4096;j++) { thermistorvals[i]+=analogReadFast(pins[i]); } } Udp.beginPacket("xxx.xxx.xxx.xxx", 19999); // Target Address, Target Port int msglen = sprintf(packetBuffer,"TM,%d,%d,%d,%d,%d,%d,%d,%f,%f#\r\n", thermistorvals[0],thermistorvals[1],thermistorvals[2], thermistorvals[3],thermistorvals[4],thermistorvals[5], thermistorvals[6], bmp.readTemperature(),bmp2.readTemperature()); Udp.write(packetBuffer,msglen); Udp.endPacket(); //Serial.print(packetBuffer); }
The data was simply collected by running nc6 -l -u -p [port_number] > log.txt in a screen session on an SBC left running 24/7 for later data analysis. I hope to take the ADC values, transform them into the thermistor resistances and then use a fifth-order fit to the manufacturer’s rounded nominal values to determine the actual temperature. Then, I would correlate the temperature agreement between thermistors as well as between the reference BMP280 sensors to see just how similar the results are. I expect that there should not be any significant differences – if things go well, the thermistors may even provide finer resolution! One will have to wait and see!
Conclusion
Thermistors being analog devices means that a whole bunch of analog considerations come into play when implementing a multi-thermistor measurement system. Thankfully, the art of multiplexing is something that is not too complicated at first glance, but when delving deeper, the choice of switching element (mechanical, electrical) can come with significant advantages and disadvantages. Similarly, the choice of using a microcontroller-integrated ADC or external ADCs can have an effect on the number of bits of resolution and noise levels, while using multiple ADCs is an option although an expensive one. Ensuring you receive the full resolution does take some care.
In the end, I did draw up a design for an IC-multiplexed solution similar to one I’ve used before for FSRs which should work but did not implement it. Instead, I implemented a straightforward system using an Arduino MKR WiFi 1010 and a lot of averaging. As the experiment would take some time to gather data for analysis, I thought it would be good to get this “meaty” blog out first … so we can chew over the results later.
[[Characterising Thermistors Blog Index]]
- Blog #1: Characterising Thermistors - Introduction
- Blog #2: Characterising Thermistors - What's In The Box?
- Blog #3: Characterising Thermistors – A Quick Primer, Beta Value & Steinhart-Hart Coefficients
- Blog #4: Characterising Thermistors – An Inconvenient Truth, Taking Things to the Fifth Degree
- Blog #5: Characterising Thermistors – Measuring Resistance Is Not So Easy!
- Blog #6: Characterising Thermistors – Is Self-Heating a Problem or Not?
- Blog #7: Characterising Thermistors – Boiling, Freezing and Zapping the Truth Out of Them!
- Blog #8: Characterising Thermistors – Practically Running Multiple Thermistors
- Blog #9: Characterising Thermistors – Multi-T Results, Insulation R Redux, 5th Order Fits & Model Performance
- Blog #10: Characterising Thermistors – Multiple Thermistors on ESP8266
- Blog #11: Characterising Thermistors – Show Me Your Curves
- Blog #12: Characterising Thermistors – Sticking Rings on Tabs & Sinks, Absolutely Crushing It!
- Blog #13: Characterising Thermistors – Pulling Out, Overload, Response Time, Building a Flow Meter & Final Conclusion
Top Comments