Reading multiple RTDs with one MAX31865?

Hello gentlemen (and ladies),

I want to read many RTDs (pt1000) using a max31865 module connected to an ESP32 SPI port. Reading one RTD is successful and steady and nice and dandy. But i need to read 3 sensors, so i saw on a PCB in an autoclave that they used ADG711 to switch between them. Those are analog switch ICs with low ON resistance (about 4 Ohms) so i decided to try that route.

I didn't have ADG711 near me, so i tried the equally same thing MAX4602, which has lower resistance (about 2.5 Ohm at 24V Vcc).

Then in software, i'm using a counter to switch between the RTDs, once a second, and read the temp, go to the next one... etc. My application will be using between 2-4 sensors so i can't just use two MAX31865. And then the trouble begins...
My minimal test code is this:

#include <Adafruit_MAX31865.h> //by adafruit

Adafruit_MAX31865 thermo = Adafruit_MAX31865(5);
#define RREF      3950.0
#define RNOMINAL  1000.0

#define ptc1Pin 25
#define ptc2Pin 33

float Ti, Tg, tmp;
uint8_t ptcID = 1; //index runs through PT sensors

//time stuff
unsigned long lastPTCUpdate;
unsigned long timeNow;
#define PTCChangeInterval 1000 //change PTC input every XX ms

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.flush();

  pinMode(25, OUTPUT);
  pinMode(33, OUTPUT);

  thermo.begin(MAX31865_2WIRE);
}

void loop() {

  timeNow = millis();

  //read temp and change ptc in intervals
  if (timeNow - lastPTCUpdate > PTCChangeInterval) { //first read temperature, then change PT ;)
    lastPTCUpdate = timeNow;

    //first read the temperature (after it's settled)
    uint16_t rtd = thermo.readRTD();
    tmp = thermo.temperature(RNOMINAL, RREF);

    if (ptcID == 1) Ti = tmp;
    if (ptcID == 2) Tg = tmp;

    Serial.print(Ti); Serial.print(","); Serial.println(Tg);

    ptcID++; // then go to next ptc

    if (ptcID == 3) ptcID = 1; //boundary condition for ptc id
    
    if (ptcID == 1) { //switch on first ptc
      digitalWrite(ptc1Pin, HIGH);
      digitalWrite(ptc2Pin, LOW);
    }
    if (ptcID == 2) { //switch on second ptc
      digitalWrite(ptc1Pin, LOW);
      digitalWrite(ptc2Pin, HIGH);
    }
  }

}

and this is the schematic (forgive my drawing skills...)


The gist of the schematic is, i'm using the left 2 channels to switch both wires of the one RTD, and the right 2 channels to switch both wires of the second RTD. The initial thing i had tried was keep one RTD wire common and just switch the second one, but that went horribly wrong.

I'm not using delays anywhere, and i avoid reading the RTDs continuously, since it seems to be a bit slow blocking the loop for a few milliseconds, so i'm sampling the temp once per second, i don't need to to be faster anyway.

But printing these two temperatures (Ti and Tg in my example) sometimes works and sometimes it generates a mess. Mostly it's a mess... i'm having one RTD in a bottle of water (should be very steady temp) and i'm heating the other one with hot air to see how the system responds. I've been trying out stuff for 3 days and just can't get it to work... losing my mind!

Samples of using the plot monitor to read stuff...



Any ideas/comments/different way of doing it are more than welcome! :blush:

Thank you for your time!

For a Rotax engine monitor, I switch sensors using: CD74HD4067 Mux and have had excellent results.

But, when you break this low-voltage signals out on breadboard, AC noise (RF hash from WiFi and smartphone) can play havoc. Thus, build on PCB or stripboard and shield and bypass the DC non-ground with a 0.01 or so cap. Metal box or other shielded enclosure a must.

Another project along the same lines but no muxing:
Serial output directly into NodeRED - Arduino for STM32 (stm32duino.com)

1 Like

Averaging AD values is a good idea unless you are working with raw AD numbers. Just create a small array and push in new and push out old.

Snippet:

#include <Streaming.h>                       // library can be found here: http://arduiniana.org/libraries/streaming/
#define DIAG false // or true

const int Average_Depth = 5 ;                // can be changed at expense of loop{} total time
int AnalogAverageMatrix [9][Average_Depth];  // 9 is based on A0, A1, A2, A3, A4, A10, A12, A13, A15

int analogAverage ( int ROW, int ApinNo )
{
    int ReadVal = 0;
    // Rollup ... small matrix, just assign for testing, could be a for() if desired
    // Oldest value is [0], newest value [4]
    AnalogAverageMatrix [ROW][0] = AnalogAverageMatrix [ROW][1];
    AnalogAverageMatrix [ROW][1] = AnalogAverageMatrix [ROW][2];
    AnalogAverageMatrix [ROW][2] = AnalogAverageMatrix [ROW][3];
    AnalogAverageMatrix [ROW][3] = AnalogAverageMatrix [ROW][4];
    // do two reads ... as a test, but one should really be enough IMO
    ReadVal = analogRead( ApinNo ); // delayMicroseconds( 10 );
    AnalogAverageMatrix [ROW][4] = analogRead( ApinNo );
    if ( DIAG) Serial << ROW  << ": " << AnalogAverageMatrix [ROW][0] << ", " <<  AnalogAverageMatrix [ROW][1] << ", " << AnalogAverageMatrix [ROW][2] << ", " << AnalogAverageMatrix [ROW][3] << ", " << AnalogAverageMatrix [ROW][4] << endl ;
    // now perform an average [0] -> [4]
    /*ReadVal = 0;
    for (int x = 0; x < 5; x++) {
      ReadVal += AnalogAverageMatrix[ROW][x];
    } */
    // Yes, a bit more flexible solution would be to loop
    ReadVal = AnalogAverageMatrix [ROW][0] + 
              AnalogAverageMatrix [ROW][1] + 
              AnalogAverageMatrix [ROW][2] + 
              AnalogAverageMatrix [ROW][3] + 
              AnalogAverageMatrix [ROW][4] ;
    ReadVal = ReadVal / Average_Depth;
    if (DIAG) Serial << "Average for " << ROW << ": " << "is " << ReadVal << endl;
    return ReadVal;     // The average is returned to calling function
}

/*  These notes need to be moved to _Notes after debugging and testing
    The test matrix is 45 integer values, arranged in 9 rows of 5 columns.
    [ROW] represents the function + specific analog input pin #
    Example:
    A0 is allocated to water temperature.  WaterTemp_AnalogIn is the pin-name variable representing A0
    Functions simply "set" ROW variable, call the analogAverage function with the analog pin name.
    ROW must be specified because the pins are not contiguous... A0, A1, A2, A3, A4, A10, A12, A13, A15 == 9 pins alias names == [0 - 8]
    The averaging matrix is hard-coded at the moment (easily expanded) to maintain 4 old values and the current analog input.
    The loop() cycle time is still under 1000mS, so the OLED displays should come to read average values in under 5 seconds.

Modified 20190712
  Base code provided by Bill as edited (arrays) version of Europa9_Mega2560 ----> Europa10_Mega2560
  
 */

You should use a 4 wire setup with the RTD , then the resistance added by the analog switch has not effect .
Leave the RTD’s continually powered and just switch across the measurement arms .
I would just switch between the rtd’s using a digital output from the processor ,then you can sync your measurements better .

Start just wiring and reading an RTD with the library example , then see if you can switch it , then add the second .

Hello again and thank you for the replies!

@mrburnette:

Averaging AD values is a good idea unless you are working with raw AD numbers. Just create a small array and push in new and push out old.

i let the MAX31865 value settle for a second before reading it out. i can't continuously do reads because at least the adafruit library is slow at reading the values and will block other important timely parts of the final code. But yes i use the general ring buffer idea whenever i can in my code.

@hammy:

You should use a 4 wire setup with the RTD , then the resistance added by the analog switch has not effect .

I'd love to, but the RTDs built in the machine that are available to me are only 2-wire...

I would just switch between the rtd’s using a digital output from the processor ,then you can sync your measurements better.

well that's exactly what my sample code does... switches RTDs every second. and as mentioned in the original post, reading one RTD works perfectly.

please note that my problem is not a mux/demux functionality. The MAX4602 i've chosen does exactly what i'm telling it to do, but there's something going on with the RTD module (max31865), it's just not consistent... in the last graph i attached, it was supposed to switch between the 2 RTDs every second, and the bottom one is submerged into water, the top one is held in hand. so the bottom values should be in an almost straight line, not going up and down, it looks like it's affected by the top RTD... i'm not sure trying out another analog mux would solve the problem...

any other ideas?

thanks again!

Make the four wire connection at the RTD terminals , that will remove lead resistance effects in your wiring and switches. You can’t get accuracy without doing this , a couple of ohms will give a large error (2.5 ohms is about 10C error )

I'll deal with that error later on, will do my own calibration with a good digital thermometer and water in various temps. A steady 2.5Ohm in a wide range of temperatures is no problem, i can offset that in code :blush:

If this means anything to anyone, for a test i replaced the MAX4602 switch IC with a simple CD4053B i had around. The code is working just fine, the result is good. Maybe it's a faulty MAX4602?

reading the 2 RTDs switching between them:

I'll patiently wait for the ADG712 to arrive to perform the same test! At least i know the code is fine :blush:

Its a measuring mess because its a stupid way from an engineering perspective, to measure temperature:

  1. What happens with the reading when the swithing process is ongoing? I can guess..
  2. Each PT100s should each be calibrated with the reference resistor on board. Probably they are not. And now you introduce four diffferent, 4 ohm +- resistances too, to the circuit.
  3. Above can be mitigated with programming, but complicated and unknown behavior in scenarious you do not grasp to see. Keep it simple stupid.
  4. I would add lack of redundancy, but that is a relativ consept.
1 Like

Agreed !

Any reason why you don't use three MAX31865 breakout boards.
SPI is a (shared) bus, and only the CS pins must be unique.
Leo..

A truth, but sometimes the 4-wire methodology is just too complicated to be utilized. A friend PhD EE instructor did some testing years ago and posted his results for thermistors.

2-wire is not stupid; stupid exists with an original design where the sensor is part of the project. With add on projects, sometimes great engineering must be reduced to just acquiring acceptable/reasonable results! This is not stupid, it is real-life engineering: said another way, it is survival.

https://www.phanderson.com/picaxe/lin_thermistor.html

@Tordens thank you for your kind suggestions, i tried what you suggested but nothing worked :rofl:
So i guess that company that uses this methodology to build medical robust autoclaves for years now is stupid too right? Yeah, they use 2-wire RTDs! It's a simple autoclave, not a rocket going to space, i don't care about .1 degrees accuracy or super-fast response. And i guess you didn't read the code at all to see what happens while the sensor is switched.

From an engineering point of view, i should use another arduino to read the max31865 because the read process is blocking, and might mess with other things later on (like buttons response) it's not as simple as the readAnalog command.

@Wawa well yeah, 2 reasons:

  1. These suckers cost triple than they used to a few years ago
  2. I'd really love to explore new ways of doing it

And as i said earlier, yes i will calibrate each and every one ptc with a good thermometer.
I'd like to hear some more constructive criticism instead of stupid this and stupid that

Yes 2 wire connections for RTD ‘s are ok if the lead resistant is small , otherwise you have to consider variations from one build to another and any temperature effects in the switching IC .
4 wire is easy to implement - the RTD is constantly powered from a current source and with separate wires you measure the. Volt drop across it .
As the measurement arms are high impedance lead resistance is insignificant . If are not interested in accuracy a thermistor may well be a better route .
Or if below 120C look at DS18B20’s.

If it’s an autoclave, I would think, all you are interested in is whether the temperature is above a certain temperature , accuracy is not important .

Did you consider switching between sensors with reed relays.
Leo..

1 Like

@Wawa i even considered switching between sensors with subminiature relays, reed relays, SSRs, but i don't know how robust they will be in the long run...

It did not work, since the cheap china modules (with 1% reference resistor....) you are using are not ment to be set in parallel operation.

The china design SPI pulldown/up resistors is not set for this. If you do a simple search online, you find those chips in 2-4-6....12 cards with proper hardware engineering. I do not care of others bad engineering designs, its on their bill, whatever expence. I say you code, it do not work and you can not find out why. Right? So is it too complicated? Why not build it easy.

In real life, 3 wire designs is mostly used. I work with such installations on a daily basis in the oil and gas industry. If the P100 sensor is 60 meters away, suddenly it is very interesting to consider the resistance the the cable. In a small Arduino bench setup for hobby testing, maybe not.

1 Like

A reed relay would be my only choice in your list.
Virtually no contact resistance, hermetically sealed, and extreme long life because in this case it's not switching power. Never heard of a failing signal reed relay, but I guess it's possible.
The mechanical lifetime (no power switching) should be in the datasheet.
In one datasheet I saw they tested 1 billion operations.
I would prefer a 0.07ohm reed relay over a 2.5ohm digital chip.
Leo..

1 Like

I defer to your experience; except for a year in EE research at uni, my real-life experiences were solely computer software and hardware.

More to the Arduino hobby usage, 2-wire and software correction (as indicated by Dr. Anderson's graph) can typically resolve to the nominal range of 1% resistors utilized. Obviously this is not going to give decimal-place accuracy.

I routinely have to remind Ops about Accuracy and Percision with their calculations on Arduinos and how to formate those Decimal places on displays when integer values are most approprimate.