How can I conver NTC voltage output to Celcius ?

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Guys,

Continuing the experiment on ADC,

I got the output from ADC already with trimpot,
Now I'm gonna change to NTC,

How can I convert the voltage output from NTC to Celcius ?

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The conversion is not straight forward because NTC thermistors are very nonlinear (resistance vs temperature).

Standard methods are described here:

http://en.wikipedia.org/wiki/The...

Yes, it involves floating point math with transcendental functions.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
The conversion is not straight forward because NTC thermistors are very nonlinear (resistance vs temperature).

Standard methods are described here:

http://en.wikipedia.org/wiki/The...

Yes, it involves floating point math with transcendental functions.

Jim


any ideas / clues for a library for NTC ?
thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//------------------ntc stuff------------------------
//      1030           4K-400
// +5---/\/\/\----+---\/\/\/\/\---+
//      rref      |      NTC      |
//                vout		       gnd

#if 0
__flash float a=1.663443e-3; //3k duralast chevy temp sensor from autozone
__flash float b=1.849225e-4;
__flash float c=5.42688e-8;
#endif

#if 0
__flash float a=1.28745e-3; //5k epcos temp sensor
__flash float b=2.357394e-4;
__flash float c=9.5052e-8;
#endif

#if 0
__flash float a=2.22348e-3; //MGB  810 ohm temperature sender
__flash float b=3.965e-5;
__flash float c=2.6583e-6;
#endif

#if 0
float a=1.778139e-3; //wells mfg tu165 from autozone $9.99
float b=1.511277e-4;
float c=9.472924e-7;
#endif

float ntcvsm; //v smoothed
float ntcrsm; //r smoothed
float ntci;   //current thru thermistor
float ntcdegc;
float ntcdegf;
//----------------------
void calcntctemp(void){
//calc temp from ntc thermistor on pin8 or pin10?
//steinhart eqn: 1/T= a+ b(LnR)+c(LnR)^3 
//20 fp ops + a log   takes 2ms
float ntcv;   //v across thermister
float ntcr;   //r of thermistor
float lnr;    //ln(r)
float lnrcu;  //ln(r)^3
float tk;     //kelvin
float oneovertk;

  addat[0]=readadchan(0);     //debug chg to ad ch 1  ad ch0 on pin8
  ntcv=addat[0]*ad2volts;     //volts across thermistor
	ntcvsm = ntcvsm + .125*(ntcv-ntcvsm); //ntcv smoothed over 8 readings
  ntci=(vref - ntcvsm)/rref;  //amps thru thermistor
  ntcr=ntcvsm/ntci;           //ohms of thermistor
	ntcrsm=ntcrsm + .125*(ntcr - ntcrsm); //ntcr smoothed
  lnr=log(ntcrsm);            //ln of ntcr
  lnrcu=lnr*lnr*lnr;          //ln ntcr cubed
  oneovertk=ntca + ntcb*lnr + ntcc*lnrcu; //the Steinhart equation!
  tk=1.0/oneovertk;           //deg in K
  ntcdegc=tk-273.15;          //deg C
  ntcdegf=1.8*ntcdegc + 32.0; //deg F
}

//-------------------------
void ntcloop(void){
//read ntc on a/d ch 0  
//2ms
char c;
unsigned int n;

  cprintf("ntc thermistor\n");
	n=1000;
	while(n--){    //took 2sec
    calcntctemp();
	}
	cprintf("ad  degc   degf   v      a      r   q=quit\n");
  while(c != 'q'){
    if(kbhit()){
      c=getchar();
    }
    calcntctemp();
    cprintf("%03x %#6.1f %#6.1f %#6.3f %#6.4f %#6.1f \r",
		  addat[0],ntcdegc,ntcdegf,ntcvsm,ntci,ntcrsm);
    delnms(10);
  }
}

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//------------------ntc stuff------------------------
//      1030           4K-400
// +5---/\/\/\----+---\/\/\/\/\---+
//      rref      |      NTC      |
//                vout		       gnd

#if 0
__flash float a=1.663443e-3; //3k duralast chevy temp sensor from autozone
__flash float b=1.849225e-4;
__flash float c=5.42688e-8;
#endif

#if 0
__flash float a=1.28745e-3; //5k epcos temp sensor
__flash float b=2.357394e-4;
__flash float c=9.5052e-8;
#endif

#if 0
__flash float a=2.22348e-3; //MGB  810 ohm temperature sender
__flash float b=3.965e-5;
__flash float c=2.6583e-6;
#endif

#if 0
float a=1.778139e-3; //wells mfg tu165 from autozone $9.99
float b=1.511277e-4;
float c=9.472924e-7;
#endif

float ntcvsm; //v smoothed
float ntcrsm; //r smoothed
float ntci;   //current thru thermistor
float ntcdegc;
float ntcdegf;
//----------------------
void calcntctemp(void){
//calc temp from ntc thermistor on pin8 or pin10?
//steinhart eqn: 1/T= a+ b(LnR)+c(LnR)^3 
//20 fp ops + a log   takes 2ms
float ntcv;   //v across thermister
float ntcr;   //r of thermistor
float lnr;    //ln(r)
float lnrcu;  //ln(r)^3
float tk;     //kelvin
float oneovertk;

  addat[0]=readadchan(0);     //debug chg to ad ch 1  ad ch0 on pin8
  ntcv=addat[0]*ad2volts;     //volts across thermistor
  ntcvsm = ntcvsm + .125*(ntcv-ntcvsm); //ntcv smoothed over 8 readings
  ntci=(vref - ntcvsm)/rref;  //amps thru thermistor
  ntcr=ntcvsm/ntci;           //ohms of thermistor
  ntcrsm=ntcrsm + .125*(ntcr - ntcrsm); //ntcr smoothed
  lnr=log(ntcrsm);            //ln of ntcr
  lnrcu=lnr*lnr*lnr;          //ln ntcr cubed
  oneovertk=ntca + ntcb*lnr + ntcc*lnrcu; //the Steinhart equation!
  tk=1.0/oneovertk;           //deg in K
  ntcdegc=tk-273.15;          //deg C
  ntcdegf=1.8*ntcdegc + 32.0; //deg F
}

//-------------------------
void ntcloop(void){
//read ntc on a/d ch 0  
//2ms
char c;
unsigned int n;

  cprintf("ntc thermistor\n");
  n=1000;
  while(n--){    //took 2sec
    calcntctemp();
  }
  cprintf("ad  degc   degf   v      a      r   q=quit\n");
  while(c != 'q'){
    if(kbhit()){
      c=getchar();
    }
    calcntctemp();
    cprintf("%03x %#6.1f %#6.1f %#6.3f %#6.4f %#6.1f \r",
      addat[0],ntcdegc,ntcdegf,ntcvsm,ntci,ntcrsm);
    delnms(10);
  }
}

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks bob ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's the function I have used :

double ntc_getSH(long adcresistence, double A, double B, double C)
{
	// use the Steinhart-Hart Thermistor Equation
	// temperature (Kelvin) = 1 / (A + B*ln(R) + C*(ln(R)^3))
	double t;
	t = log( adcresistence );
	t = 1 / (A + (B * t) + (C * t * t * t));
	t = t - 273.15; // convert Kelvin to Celcius
	//t = (t * 9.0) / 5.0 + 32.0; // convert Celcius to Fahrenheit
	return t;
}
main()

char volts[50];
	char tempCelcius[50];
	int  adc_result;
	float adcA;
	long adcresistance;
	double d;

//check the adc result begin
			adc_result=read_adc();
			adcA = (int)(adc_result*5.0)/1024.0;
			
			if (adcA != 0)
			{
				 //itoa(adcA,volts,5);
				sprintf(volts,"adc=%.5f volt",adcA);
				lcd_string(volts);
				_delay_ms(2000);
				//measure temperature
				lcd_cmd(0x80);//put the cursor into the first row
				_delay_ms (10);
				lcd_cmd(0x01);//Clear display
				adcresistance = (long)((long)(1023*10000)/adc_result-10000);
				d = ntc_getSH(adcresistance, (double)0.947070725e-3, (double)2.450662058e-4, (double)1.853992838e-7);
			    sprintf(tempCelcius,"temp=%.5f C",d);
				//display temp to LCD
				lcd_string("Temp Value");
				lcd_cmd(0xC0);//goto second row
				//lcd_string("Value of PF0");
				_delay_ms(1000);
				lcd_string(tempCelcius);
				_delay_ms(1000);
			}
			else
			{
				lcd_string("No Result!");
				_delay_ms(2000);
			}
			//check the adc result end

Does anyone know why I get "nan" value on temperature ?
Did I miss something here ?

thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know which Compiler you are using.
I suggest that you use double or float consistently.

Personally, I would just use double. Then if I use a compiler with real 64-bit doubles, the code would work the same as on a "32-bit double" system. e.g. I would tell printf() to use the "lf" format.

Incidentally you don't need to cast the f-p constant arguments as double. The ntc_getSH() forward declaration should ensure the correct argument types are used. When you use inappropriate casts, you remove the Compiler's type consistency checking.

Your code 'looks' ok to me. However, I have no idea what values you are using. Trace the calculation in the Simulator. You need some pretty extreme values to get a f-p NaN.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One detail. You have

adcresistance = (long)((long)(1023*10000)/adc_result-10000); 

The inner expression "1023*10000" is int*int and will overflow (and later converted to a long). Either move in the nearest (long) or write 10230000 or 1023L*10000 or remove the inner pair of parenthesis.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Make a look up table with ADC in and deg. out.
Calc. the table in excel or a similar program.
If it gets to big with all values, then make 32 or so entries with value, and coefficient , then the math can be done by interpolation in simple integer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

snigelen wrote:
One detail. You have
adcresistance = (long)((long)(1023*10000)/adc_result-10000); 

The inner expression "1023*10000" is int*int and will overflow (and later converted to a long). Either move in the nearest (long) or write 10230000 or 1023L*10000 or remove the inner pair of parenthesis.

I see, thanks for your suggestion.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

snigelen wrote:
One detail. You have
adcresistance = (long)((long)(1023*10000)/adc_result-10000); 

The inner expression "1023*10000" is int*int and will overflow (and later converted to a long). Either move in the nearest (long) or write 10230000 or 1023L*10000 or remove the inner pair of parenthesis.

becoming like this ?

adcresistance = (long)(10230000/adc_result-10000);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

it works, but strangely, when I tested it, the temperature should go up, but displaying go down, is it because I put the resistor in reverse ??
thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would agree that interpolation with integers is the fastest and most efficient method.

OTOH, human display on LCDs really don't need to be fast. So I would just "tidy up" the code and take snigelen's advice :

   char volts[50];
   char tempCelcius[50];
   int  adc_result;
   double adcA;
   long adcresistance;
   double d;

//check the adc result begin
         adc_result = read_adc();
         adcA = (adc_result * 5.0)/1024.0;  // actual voltage
         
         if (adcA != 0)
         {
            sprintf(volts, "adc=%.5lf volt", adcA);
            lcd_string(volts);
            ...
            // I don't understand YOUR expression
            adcresistance = ((10230000L)/adc_result-10000);
            // surely R = V / I
            adcresistance = adcA / 0.0001;   // for 100uA current
            d = ntc_getSH(adcresistance, 0.947070725e-3, 2.450662058e-4, 1.853992838e-7);
            sprintf(tempCelcius, "temp=%.5lf C", d);
            //display temp to LCD

Personally, I would choose names like volts or millivolts for your f-p variables. The display string representation might be volt_string.

I don't understand your adcresistance calculation.

David.

Last Edited: Sun. May 26, 2013 - 09:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thank very much guys, it works as expected...