Pt100 code using direct mathematical equation & Piecewise Linear Approximation

Thread Starter

Vindhyachal Takniki

Joined Nov 3, 2014
598
1. I have made to code to convert RTD resistance into temperature. It is attached. It used direct mathematical method. made using Analog device AN709.

2. Code is also measuring time in which conversion is done. Surprisingly for negative temperature values, it takes around 280us max & for positive temperature it takes around 144us max.
Code is run on arduino Uno.
I thought it would take much higher time as lots of floats are involved.

3. Just made some random resistance value at step of 0.29 to see how much range is covered.

4. Any sample code in c for Piecewise Linear Approximation method?
In AN709, link point takes to different page.
I wanted to make code on array based also, as it will much less memory & less time.


5. Also in function
static uint8_t pt100_res_to_temp(float res , float *r_tempc)

at starting i had checked for res parameter is negative, if its negative passed by error, system dont make incorrect calculations.
Now I want to check if res passed is NaN or not.
This is done so that all the correct/incorrect values checked should be tested in this function & function always return correct error codes & values.
Is this step correct to check if number is NaN

if(res != res)
{
}

Or some compilers may optimize it away?
if its incorrect what step should I use?


Code:
typedef float  float32_t;
#define f32_t  float32_t

#define RTD_NOM  (100.0f)

#define RTD_A    ((f32_t)3.9083e-3)
#define RTD_B    ((f32_t)-5.775e-7)
#define RTD_C    ((f32_t)–4.183e–12)  /* not used */

#define RTD_Z1   (-RTD_A)
#define RTD_Z2   ((RTD_A * RTD_A) - (4.0f * RTD_B))
#define RTD_Z3   ((4.0f * RTD_B) / RTD_NOM)
#define RTD_Z4   (2.0f * RTD_B)

#define RTD_C0   ((f32_t)-242.02)
#define RTD_C1   ((f32_t)+2.2228)
#define RTD_C2   ((f32_t)+2.5859e-3)
#define RTD_C3   ((f32_t)-4.8260e-6)
#define RTD_C4   ((f32_t)-2.8183e-8)
#define RTD_C5   ((f32_t)+1.5243e-10)




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

}

void loop()
{
    uint8_t err;
    uint32_t old_time, new_time;
    float resistance = 0.0f , tempc;
    uint16_t cnt;
   
    for(cnt = 0U ; cnt < 1360U ; cnt++)
    {
        resistance = cnt * 0.29f;
        old_time = micros();
        err = pt100_res_to_temp(resistance , &tempc);
        new_time = micros();
   
        Serial.println();
        Serial.print("Sno: ");
        Serial.print(cnt);
        Serial.print(" ; ");
        Serial.print("Time(us): ");
        Serial.print(new_time - old_time);
        Serial.print(" ; ");
        Serial.print("Resistance: ");
        Serial.print(resistance , 8);
        Serial.print(" ; ");
        Serial.print("Temperature: ");
        Serial.print(tempc , 8);
        Serial.print(" ; ");  
        Serial.print("Error: ");
        Serial.print(err);
    }

    while(1);

}


static uint8_t pt100_res_to_temp(float res , float *r_tempc)
{
    uint8_t out_of_range = 0U;
     
    float32_t temp;
    float32_t res_square;
    float32_t res_cube;
    float32_t res_quad;
    float32_t res_pent;

    if(res < 0.0f)
    {
        out_of_range = 1U;     /* resistance out of bound on lower side, cant be negative */
        temp = -999.999f;          
    }
    else
    {     
        temp = RTD_Z2 + (RTD_Z3 * res);
        if(temp < 0.0f)
        {
            out_of_range = 2U;     /* resistance out of bound on higher side */
            temp = +999.999f;
        }
        else
        {
            temp = ( RTD_Z1 + sqrt(temp) ) / RTD_Z4;
   
            if(temp < 0.0f)    /* if negative temp */
            {
                res_square =  res * res;
                res_cube   =  res_square * res;
                res_quad   =  res_cube   * res;
                res_pent   =  res_quad   * res;
   
                temp = +(RTD_C0)
                       +(RTD_C1 * res)
                       +(RTD_C2 * res_square)
                       +(RTD_C3 * res_cube)
                       +(RTD_C4 * res_quad)
                       +(RTD_C5 * res_pent);
            }         
        }
    }

    *r_tempc = temp;

    return out_of_range;
}
 

WBahn

Joined Mar 31, 2012
32,823
While I haven't done much with NAN, my understanding is that, aside from using functions (which may be macros) such as isnan(), testing for x != x is pretty much the standard way. As such, I don't think the compiler can optimize it away. The compiler writer should know that this expression can evaluate to true for this data type.
 
1. I have made to code to convert RTD resistance into temperature. It is attached. It used direct mathematical method. made using Analog device AN709.
/-/
4. Any sample code in c for Piecewise Linear Approximation method?
In AN709, link point takes to different page.
I wanted to make code on array based also, as it will much less memory & less time.
/-/
Well, maybe, sort of....
A little over three years ago, I was playing around with a thermistor and a Galileo Generation 1 board. This was actually one of the first project programs I tried to write in "arduino". I briefly looked through the AN you linked and it sort of meets that piecewise criteria...at least to me. I have to state that I was working purely intuitively and really just playing around. Your post reminded me of this and I dug it up and tested it out with an arduino uno. Had to change a few lines from the Galileo code. You might find it interesting.

I used a 1 K NTC with a 1 K resistor on the bottom.

First I plotted a cubic polynomial over the whole range.
Full Range 800.jpg

As you might expect, the fit is poor, especially at the low temperature range. So I divided the curve into three separate portions and fit each to a cubic polynomial (inverse).

From the code, these were the coefficients and the ohm ranges.
float y01=30.1692,a1=9275.0849,b1=-302921.5404,c1=3725393.4632; // 25.6-574 ohms (40 to 150 C)
float y02=-31.8204,a2=111301.3283,b2=-72815227.8770,c2=18819673720.8152; // 575-7569 ohms (-20 to 40 C)
float y03=-61.0910,a3=634759.5304,b3=-3734660927.1959,c3=9721900000000.0; // 7570-23342 ohms (-20 to -40 C)

Here is the low range:
LowRange800.jpg

Mid-range:
MidRange 800.jpg

and High range:
HighRange 800.jpg

I was happy with the R2 values.

Here is the code, simple as it is...
Code:
// Thermistor calculation using three 3rd degree polynomials
//
//globals
// coefficients in the fit equations - temp=y0+(a/x)+(b/x^2)+(c/x^3)
float y01=30.1692,a1=9275.0849,b1=-302921.5404,c1=3725393.4632;  // 25.6-574 ohms (40 to 150 C)
float y02=-31.8204,a2=111301.3283,b2=-72815227.8770,c2=18819673720.8152;  // 575-7569 ohms (-20 to 40 C)
float y03=-61.0910,a3=634759.5304,b3=-3734660927.1959,c3=9721900000000.0;  // 7570-23342 ohms (-20 to -40 C)
float GalVout=5.045;  // Measured Vout on my Gal is 5.045
//float A2Dres=4095;  // Gal is 12 bits
float A2Dres=1024;
int ReadPin=2;  // midpoint of resistor divider goes to GAL A2
float Rf=1001.0;  // fixed resistor value (ohms)
float Ao;  // analog out value (analogRead(ReadPin))
float Vr;  // voltage (volts) at divider midpoint
float Rt;  // calculated resistance (ohms) of thermistor
float TempC;
float TempF;
double startt,endt;

void setup() {
Serial.begin(9600);
// for Galileo only
//analogReadResolution(12);  // set full resolution
delay(3000);
Serial.println("Thermistor demo (3rd degree poly fit)");
Serial.println("\n  Ao  Rt  Temp [C]  Temp [F]");
}

void loop() {
  Ao=analogRead(ReadPin);
  Vr=(Ao/A2Dres)* GalVout;
  Rt=((GalVout * Rf) / Vr)-Rf;
  if (Rt>7570) // 7570-23342 (-20 to -40 C)
  {
  TempC=y03 +(a3/Rt) + (b3/pow(Rt,2.0)) + (c3/pow(Rt,3.0));
  }
  else if (Rt>575) // 575-7569 ohms (-20 to 40 C)
  {
  startt=micros();
  TempC=y02 +(a2/Rt) + (b2/pow(Rt,2.0)) + (c2/pow(Rt,3.0));
  endt=micros();
  }
  else // 25.6-574 ohms (40 to 150 C)
  {
  TempC=y01 +(a1/Rt) + (b1/pow(Rt,2.0)) + (c1/pow(Rt,3.0));
  }
  TempF=((TempC * 9.0) / 5.0 )+32.0;
  Serial.print("  ");
  Serial.print((int)Ao);
  Serial.print("  ");
  Serial.print(Rt);
  Serial.print("  ");
  Serial.print(TempC);
  Serial.print("  ");
  Serial.print(TempF);
  Serial.print("  ");
  Serial.print(endt-startt);
  Serial.println("us");
  delay(3000);
}
You might notice that I am using the much-maligned POW, but it is easy. Tonight, I slapped two micros() around one range and got values in the range of 384-512 us. Pretty slow eh?

It was a fun project.
 
Last edited:

jayanthd

Joined Jul 4, 2015
945
The LTC2053 Proteus model has some problem. It makes the simulation run very slow. You can see the simulation time in Proteus. In hardware I am sure it will work fast.

It is not a problem because temperature doesn't change rapidly.

I have used direct equation to get the readings.
 
Not sure if the OP has drifted away, but I believe that source code for Piecewise Linear Approximation which accompanies AN-0970 is available here (I haven't checked it out yet though). Also available is source code to accompany AN-709.
 
Top