Automotive temperature sensors interface

Below is some tested and robust code for accurately measuring two different automotive temperature sensors; the GM 12146312 and Stewart Warner 33..400 ohm sensors. Both of these are NTC (negative temperature coefficient) temperature-dependent resistors and require a polynomial to map resistance to temperature. This code uses the common Steinhart-Hart NTC polynomial and coefficients I found on the net.

This thread was inspired by this thread from a few years ago, now closed. There's a typo in the polynomial (misplaced paren). GM Coolant Sensor #12146312 Interface

One side of the sensor is connected to ground (The S.W. sensor body is ground) and the other to the Arduino analog input. A single resistor from the sensor/input pin to 5V or 3.3V ("pull up") is required. That's it.

My solution is slightly more complicated than what is possible but I think you'll see it's easier in the end -- I first calculate the sensor's resistance in ohms, then map that resistance to temperature (degrees F or C). The reason for this is that the fundamental calculation does not need to be changed to accommodate whatever arbitrary pull up resistance or voltage is used.

There are two subroutines; one that reads the analog pin and determines resistance, and another that converts resistance to temperature reading, given the Steinhart-Hart coefficients.

Be warned that all of these automotive sensors are not very accurate at temperatures below 100F. Engine systems are concerned with over 100F up to 300F or so; below 100, eg. room temperature, they're not very good at all. So don't let testing on the bench fool you that they're not working.

These parameters go at the top of the source and define your electrical setup. ADCFULLSCALE is typical analogRead() maximum value.

// Sensor parameters. Sensors are resistive, ground connected, and pulled
// up to (in this case) 5V with 470 ohms.
//
const float ADCFULLSCALE =          1024;     // analogRead() returns 0..1023
const float SENSORPUVOLTS =            3.3;   // volts
const float SENSORPUOHMS =           330;     // pullup resistor

// These are the Steinhart-Hart coefficients for the two sensor types.
//
const double SWCoeffA = 0.0007350115860272266;
const double SWCoeffB = 0.00042333918838882934;
const double SWCoeffC= -2.005500370510285e-7;

const double GMCoeffA = 0.001129148;
const double GMCoeffB = 0.000234125;
const double GMCoeffC = 0.0000000876741;


This is how a Stewart-Warner 100 to 280F temperature sensor is read. Returns integer degrees F. Pin is the analog input pin number. The text is for printing debug info.

readNTCSensor (pin, "SW oil temp", SWCoeffA, SWCoeffB, SWCoeffC));

This is how a GM #12146312 temperature sensor is read. Returns integer degrees F. Pin is the analog input pin number. The text is for printing debug info.

readNTCSensor (pin, "GM coolant temp", GMCoeffA, GMCoeffB, GMCoeffC)

This is the code that does the calculation.

int readNTCSensor (int pin, const char *text, double kA, double kB, double kC) {
double Rs;
double logR2, T;

  Rs= readSensor (pin);

  // Steinhart-Hart NTC thermistor resistance to degrees F.
  // Steinhart - Hart Equation = 1/T = A+B(LnR)+C(LnR)3
  //
  logR2= log (Rs);
  T= 1.0 / (kA + (kB * logR2) + (kC * logR2 * logR2 * logR2));
  T= T - 273.15;                   // K to C
  T= (T * 9.0) / 5.0 + 32.0;       // C to F

  return T + 0.5;
}


/* read a resistive sensor pulled up with a resistor, return it's resistance, in ohms. */

int readSensor (int pin) {
int a;
float adcV, I, Rs;

  // voltage at the resistor divider. if the sensor is open
  // circuit, this should be the same as the reference
  // voltage.
  //  
  a= analogRead (pin);
  adcV= (float)a * (SENSORPUVOLTS / ADCFULLSCALE);

  // current flowing through the sensor and the pullup.
  // the battery voltage is in millivolts. this won't work out
  // well if the sensor is infinite ohms, hence the max (i, .0001)
  //
  I= fmax ((SENSORPUVOLTS - adcV) / SENSORPUOHMS, 0.0001);

  // sensor R now derives from I.
  //
  Rs= adcV / I;

  return Rs + 0.5;
}

2 Likes

5volt or 3.3volt would depend on which processor logic and which Aref voltage.
To be stable, the pull up resistor must be powered from the same Aref voltage*.
If not, then ratiometric behaviour is lost.

Ground currents could stuff up readings, therefore Arduino ground should be connected (as close as possible to sensor ground.
Leo..

*Same Aref voltage does not mean the physical Aref pin.

Yes, of course, but thank you for pointing that out. When posting things like this it's hard to know how much basic knowledge to assume. I'm assuming here that if you need to use auto NTC sensors, and you can read the code, you can figure it out; there's only three const dependencies there.

(In my specific case -- I'm reusing a box I built in 2014 -- its' a Mega 2560, but using EXTERNALREFERENCE and a separate 3.3V "reference". A couple percent accuracy is perfectly fine here.)

Yes, good wiring (and grounding) practice is required, but that's outside the scope of the code.

The GM sensor connector has two pins, so no inherent ground, so you can run that one's return back to the arduino. The Stewart-Warners, the body is ground, so that's that.

In my automotive projects, for boxes with mixed sensors and outputs (cooling pump and fan, and temp sensors) good practice starts with design -- I don't use low-side MOSFETs, only high-side drivers, with ground connected loads (motors). This means nearly zero current in the leads to the CPU box, all of the current (and noise and spikes and droop) are in the "battery" leads and the device's ground leads, and the metal chassis. This hugely eliminates ground current, by design.

LOL, now I'm way off code too, so I can't complain about your comment! (lol I'm not!). It all does interact, don't it... : - )

Given that these sensors are probably not hugely accurate , I would probably calibrate using a pan of water and a thermometer . Warm the water upto boiling taking readings ( better to use a thermos flask )
Put the results into spreadsheet and curve fit the area of interest

I've found them to be accurate. I've done heated pan of water (and woods metal for over 212F) and they're fine. There's many millions of these things in use.

  Stewart-Warner 33..240 test data derived from heated beaker of water, stirred,
  using laboratory mercury thermometer and Fluke 115 as ohmmeter.

  T(c)  T(f)   R (ohms)
  25    77    544
  35    95    403
  45   113    326
  50   122    287
  55   131    258
  60   140    229
  75   167    166
  85   185    137
  90   194    123
  95   203    110
  100  212    103.7

Do you understand the ratiometric behaviour I was talking about?
It seems not many people do.

If sensor power and Aref voltage come from the same point,
then absolute voltage (stable reference) does not matter.
Leo..

Yes, of course. But that belongs in a fundamental discussion of how analogRead() works.

Don't be silly. Your code ignores the fact the the sensor and the ADC are ratiometric, and you require the user to enter the voltage reference value.

That requirement is completely unnecessary, and to me that strongly suggests that you do not understand how to use ratiometric sensors and ADCs properly.

Aww come on, did you read the code? YES it wants to know reference voltage, and the pull up resistor's value, so that it can use Ohm's Law to calculate the sensor's resistance.

This works, is portable, and rigorous. Maybe you're too stuck on your fixed ideas, which are correct, within a limited scope.

This demonstrably works. Maybe you don't understand how.

To test, substitute a variable resistance for the sensor, put readSensor() in a loop an print out it's values. Disconnect and measure with a DVM. You'll see it works.

YES YES it is dependent on the precision and stability of the reference; it is not fully "ratiometric" in that sense.

It is correct that if my version of "reference" voltage drops 2%, the measured resistance will be off by 2%. This is not a problem in and of itself.

(A later version of my own board measures this "sensor reference" voltage, and uses THAT in the calc instead of the const. This eliminates all error in that chain, above.)

But you're being pedantic here.

Another indicator that you don't understand ratiometric behaviour.

Absolute voltage is irrelevant when calculating sensor resistance.
You get the same result if pull up voltage and Aref is 2.5volt or 5.2volt.
Leo..

The point is completely lost on the OP.

See if this is enough to calculate thermistor value.
The 10-bit ratiometric A/D of an Arduino returns 256.
The pull up resistor is 330 ohm.
Leo..

First, here I posted a corrected NTC Steinhart-Hart calculation, and provided coeff's for SW sensors also. And a how-to-use fragment. Here you are criticizing my circuit design, for which I posted the caveats for it's sensitivity to "ref" voltage accuracy.

You are flatly wrong about the above -- to calculate an unknown RESISTANCE in a voltage divider you need to know supply V and one R, and measure the voltage at the junction. From that you can calculate the unknown R.

You repeat the most simplistic case of putting a pot on VREF and the wiper on analog in and for this case and similar you are correct as noted.

In an automotive system sensors are very low resistance, and car enviro's are noisy and prone to problems nt seen on the bench. With 6, 8 sensors with 3 or 5V pullup, if all sensor inputs were grounded at once, it would exceed the current capability of the onboard ref, and using it would put the onboard ref and it's bypass coupling to the CPU board susceptible to spikes etc on any sensor wire or bundle. For these two reasons alone this articular app has a separate small supply for "reference" -- inaccuracies casually noted in the original post. The inputs from my sensors, not stated until now, involve passive networks for noise/spikes, an opamp buffer, etc before the CPU.

None of this is pertinent to the post, yet you make your trivial attacks on my electrical design, in a post about NTC calcs.

Pedants like yourself are why this will be my last post to the Arduino forum. You have very high karma, post constantly, picking at small things, wearing people down. You're not the first I've come across. You just never let things alone.

So here, you can "win", and be one of the reasons people are declining to post. What novice wants to be exposed to withering criticism? Are you so certain of yourself, that there is only this one way to use the ADCs?

You COULD HAVE pointed out the shortcomings -- even though I noted them, about accuracy etc -- I would have agreed with you, readers of this thread could have got what they would -- hopefully the NTC calcs -- and moved on. Instead here were are beating a dead horse.

You win. Have the last reply if you like.

1 Like

I know that the answer from post#12 is 110 ohm.
And I got there without knowing "voltage".

The ratio of (A/D value) to (10-bit A/D - A/D value) is 1:3
therefore the ratio of thermistor to resistor value must also be 1:3

If I connect a pot to an Uno, and turn it halfway, then I know the A/D will return 512.
I don't have to know pot voltages for that.
Leo..

But i was deriving RESISTANCE of the sensor, not just its PROPORTION of the reference voltage. For a very particular reason that you just keep skipping over. Of course that works -- it does not give me RESISTANCE IN OHMS, which I then map USING STANDARDIZED TABULAR DATA to sensor meaning. I said this in the very first post. Maybe not clearly enough, truly. But rather than saying "why don't you just do it as a proportion?" or "why go through the trouble to calculate R when this other is easier?" you just said 'you're wrong' like it's a competition or something.

(The advantages during development and later testing, of mapping resistance to value are huge. But maybe you don't develop and deploy software and hardware systems in demanding environments. I do.)

In my frustration I made a typo, corrected in previous post and here:

to calculate an unknown RESISTANCE in a voltage divider you need to know supply V and one R

Sheesh. Enjoy yourself.

1 Like

That should be
to calculate an unknown RESISTANCE in a voltage divider you only need to know R

One day you will find out that ratio is exact and voltage is not.
Good luck with your project.
Leo..

Well, you need to know V, but people who understand how to use ratiometric ADCs for the voltage measurement do not.

Simple example: The ADC reports the ratio 512/1024 (half the ADC reference voltage), measured at the junction of a voltage divider powered by the ADC reference voltage, consisting of R1 = 1K and unknown R2.

Explain why the unknown R2 must be 1K.

Bonus points: explain why the value of AREF is irrelevant, and show that this approach works to determine the unknown value of R2, for any ratio reported by the ADC.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.