STEINHART-HART COEFFICIENTS for Thermistors

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

I have these STEINHART-HART COEFFICIENTS for a thermistor and have no idea how to use them to linearize my 10 bit result that so far I send to an LCD.

a = 0.00112485;
b = 0.000234822;
c = 0.000000085;

From AD0 I am reading 0-1023 depending on the temperature with 600 being around 25c.

I did study an example from Michael Spiceland which I have attached who uses a different thermistor but in his example he has four fields for the Thermistor Coefficents where mine just has three, I am trying to get the temp from a device who's thermistor is glued in so I cannot use another one.

Also, on the Atmega128, it has Vref. Is this for feeding an external voltage reference into the AD, If so I am wondering if more accurate/stable results from the thermistor would be possible if I used say a 2.5V precision reference instead of VCC.

Any help appreciated.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//----thermistor stuff----------
#define C2K 273.15	
#define FIVEOVERNINE .5555555
#define NINEOVERFIVE 1.8
#define FIVEOVER1023 .05

float adavg;
float ntcv;
float ntcr;
float rref,ntci;
float tk,tc;
float tf;
float lnr,lnrcu;
float oneovert;
//-------------------------
void readthermistor(void){
//read thermistor
//steinhart eqn:   1/T= a+ b*LnR + c*LnR^3  
//rref is in series with thermistor from VCC

  addat[0]=readadchan(0);
    adavg += (addat[0]-adavg)*0.125;
    ntcv = adavg*FIVEOVER1023; //v across thermistor
    ntci = (5.0-ntcv)/rref;    //i thru thermistor (use actual vcc rather than 5.0)
    ntcr = ntcv/ntci;          //r of thermistor
    lnr = log(ntcr);           //ln of r
    lnrcu = lnr*lnr*lnr;       //(ln of r)^3
    oneovert =a + b*lnr + c*lnrcu;
    tk = 1.0/oneovert;         //kelvin
    tc = tk - C2K;             //celcius
    tf = NINEOVERFIVE*tc + 32.0;  //fahrenheit
}

//-------------------------
void thermistorloop(void){
//read thermistor in loop
char ch;

  cprintf("v       r       tc      tf\n");
//  a= 1.197529e-3;   //from US sensors applet
//  b= 2.405050502e-4;
//  c= 1.92392e-7;

  while(ch != 'q'){
    readthermistor();		
    cprintf("%#7.3f %#7.1f %#7.1f %#7.1f  \r",ntcv,ntcr,tc,tf);
    if(kbhit()){
      ch=getchar();
    }
  }
}

Imagecraft compiler user

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

Thanks bob but that's confused me like crazy.

I have been making some progress with this code although my results are a little ooooff.

And also, am trying to get results with .1 or better resolutions.

ActualTemp = Thermister(ADCvalue);
// Returns 37 at 27.8 celcius
	
Resistance=((10240000/ADCvalue) - 10000); 
// Returns 5700 to 5800 at 27.8 celcius

/*
A simple little application to test temperitures on an atmega128/16.
*/

#include 
#include  
#include 
#include 
#include 
#include "lcd.h"
#include 
#include 
#include 
#include 
#include 
#include 



void was_LCD_connected(); 			
// A function that tests if the LCD is plugged in
bool LCD_CONNECTED; 				
// a Variable set by is_LCD_connected 1=connected:0 is missing 
volatile uint16_t word;


long Resistance;
double Temp;



float GetADCFromChannel(uint8_t ADC_Channel)
	{
    int ADCvalue;

    ADMUX = ADC_Channel;         			
// Select from ADC Channel specified

    ADMUX |= (1 << REFS0);    				
// use AVcc as the reference

    ADMUX &= ~(1 << ADLAR);   				
// clear for 10 bit resolution
 
    ADCSRA|= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);    
// 128 prescale for 16Mhz  = 125Khz.

    ADCSRA |= (1 << ADEN);    				
// Enable the ADC

    ADCSRA |= (1 << ADSC);    				
// Start the ADC conversion

    ADCvalue = ADCL;

    ADCvalue = (ADCH << 8) + ADCvalue;    
// ADCH is read so ADC can be updated again

    return ADCvalue;
	}


float Thermister(int RawADC) 
	{

  	Resistance=((10240000/RawADC) - 10000);
  	Temp = log(Resistance);
 	Temp = 1 / (0.00112485 + (0.000234822 * Temp) + (0.000000085 * Temp * Temp * Temp));
 	Temp = Temp - 273.15; 
// Convert Kelvin to Celsius
 
	//Temp = (Temp * 9.0)/ 5.0 + 32.0;                 
// Here if result should be Fahrenheit.
	return Temp;
	}


int main(void)
{


	char ws[50];
	int ADCvalue;
	float ActualTemp;

	was_LCD_connected();			
// Checks if the pyhsical connector of the LCD is in Place

	if (LCD_CONNECTED) 
		lcd_init(LCD_DISP_ON);						
		lcd_clrscr();							



	while(1)
	{


		if (LCD_CONNECTED) 
		{


        ADCvalue = GetADCFromChannel(0); 
// 5V = 1023 which is 10 bits. 0V = 0
 
  		
		ActualTemp = Thermister(ADCvalue); 			
// Returns 37 at 27.8 celcius
		itoa(ActualTemp, ws, 10); 
		lcd_gotoxy(0,0);  						
		lcd_puts("ADCvalue=");
		lcd_puts(ws); 
		lcd_puts("  ");
		
	
		Resistance=((10240000/ADCvalue) - 10000); 
// Returns 5700 to 5800 at 27.8 celcius
		itoa(Resistance, ws, 10); 
		lcd_gotoxy(0,1);  						
		lcd_puts("Resistance=");
		lcd_puts(ws); 
		lcd_puts("  ");


		}	

	}

}

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

You need the resistance of the thermistor. If you have a known R from a known V to the thermistor, and read the volts across the thermistor, you can also calc the I thru it because of the known R above it. See how my program calculated R by using I and V?

Imagecraft compiler user

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

Quote:
If so I am wondering if more accurate/stable results from the thermistor would be possible if I used say a 2.5V precision reference instead of VCC.

2V5 is better than Vcc, mainly because of the reduction of self-heating of the thermistor you use. What value of pullup resistor do you use ? Be sure you use a pullup resistor that is accurate enough

Are you sure its only one set of a/b/c constants ? The thermistor I use (thermometrics) has 4 sets of a/b/c/d, each for another temperature range.

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

US Sensors has an applet where you enter 3 temperatures and 3 resistances from the sensor (like at 0 deg, 25 deg, and 100 deg) and it calcs the a,b, and c.

Imagecraft compiler user

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

Yes it has just three constants, I am almost there but just seam to be calculating wrong some place.

Paddy wrote:
Quote:
If so I am wondering if more accurate/stable results from the thermistor would be possible if I used say a 2.5V precision reference instead of VCC.

2V5 is better than Vcc, mainly because of the reduction of self-heating of the thermistor you use. What value of pullup resistor do you use ? Be sure you use a pullup resistor that is accurate enough

Are you sure its only one set of a/b/c constants ? The thermistor I use (thermometrics) has 4 sets of a/b/c/d, each for another temperature range.

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

Well I am going insane here.

I am trying getting the voltage from the ADC but end up with something strange when writing to the LCD.

Been searching all day on the net to try and figure this one out.

My ADCvalue is 622

So 622 * 4.76 / 1024 should be 2.89

Actually as I write this, and do it with a calculator the result is "2.891328125" could it be I am not allowing enough space in WX and its wrapping around?

On the LCD I get "V=2.XX" the two XX being garbled characters.

Am using AVR-GCC with winAVR.


VOLtage = (ADCvalue * 4.76) / 1024;
		
char WX[20];

itoa(VOLtage, WX, 10); 
		lcd_gotoxy(10,1);  			
lcd_puts("V=");

lcd_putc(WX[0]);
lcd_putc('.');
lcd_putc(WX[1]);
lcd_putc(WX[2]);
lcd_puts("  ");

Also if I try it this way I get compiler errors:

   VOLtage=(ADCvalue * 4.76) / 1024;
   sprintf(WX,"%.2f    ",VOLtage);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

if VOLtage is an integer then the result of (622 * 4.76) / 1024 is indeed 2. I suspect the garbled characters are a result of WX[1] & WX[2] being empty/undefined.

What compiler errors do you get exactly?

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

maximax wrote:
if VOLtage is an integer then the result of (622 * 4.76) / 1024 is indeed 2. I suspect the garbled characters are a result of WX[1] & WX[2] being empty/undefined.

What compiler errors do you get exactly?

If I do it with sprintf I get these errors.

Build started 30.8.2011 at 23:00:25
avr-gcc -mmcu=atmega128 -Wall -gdwarf-2 -std=gnu99 -DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT test_lcd.o -MF dep/test_lcd.o.d -c ../test_lcd.c
avr-gcc -mmcu=atmega128 -Wall -gdwarf-2 -std=gnu99 -DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT lcd.o -MF dep/lcd.o.d -c ../lcd.c
avr-gcc -mmcu=atmega128 -Wl,-Map=test_lcd.map test_lcd.o lcd.o -o test_lcd.elf
c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr51\libc.a(inverse.o): In function `inverse':
(.text.avr-libc.fplib+0xc): relocation truncated to fit: R_AVR_13_PCREL against symbol `__divsf3' defined in .text section in c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr51\libgcc.a(_div_sf.o)
c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr51\libc.a(modf.o): In function `modff':
(.text.avr-libc.fplib+0x3e): relocation truncated to fit: R_AVR_13_PCREL against symbol `__subsf3' defined in .text section in c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr51\libgcc.a(_addsub_sf.o)
make: *** [test_lcd.elf] Error 1
Build failed with 1 errors and 0 warnings...

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

Hi Maximax, VOLtage was indeed an int so I tried a float and still get the same problem.

I get V2. and two characters that look like palm trees, so I think you are spot on about the 2.XX so its likely the 2.89088908 as an example is not getting loaded into WX as 289088908 which I would expect.

Quote:

if VOLtage is an integer then the result of (622 * 4.76) / 1024 is indeed 2. I suspect the garbled characters are a result of WX[1] & WX[2] being empty/undefined.

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

My palm trees instead of digits, see attached photo.

Attachment(s): 

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

lasershows wrote:
so I tried a float and still get the same problem.

if you mean the first example, itoa does not accept a float, only int!

What happens if you use sprintf with the float?

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

maximax wrote:
lasershows wrote:
so I tried a float and still get the same problem.

if you mean the first example, itoa does not accept a float, only int!

What happens if you use sprintf with the float?

I get ../test_lcd.c:123: warning: format '%.2f' expects type 'double', but argument 3 has type 'float'

If I change it to double I get the same compile errors as above.

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

Am just wondering something could it be the AVR-GCC and WInavr are not supporting these directives in sprintf

AVR-GCC 4.18.684
WIN-AVR 20100110

Are these old?

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

lasershows wrote:
Am just wondering something could it be the AVR-GCC and WInavr are not supporting these directives in sprintf

I will have to leave that one to the pros, but here is an alternative that does not involve the use of floating point math.
Off the top of my head and untested!

long VOLtage = (ADCvalue * 476) / 1024;

char WX[20];
itoa(VOLtage, WX, 10);

lcd_gotoxy(10,1);           
lcd_puts("V=");
lcd_putc(WX[0]);
lcd_putc('.');
lcd_putc(WX[1]);
lcd_putc(WX[2]); 

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

maximax wrote:
lasershows wrote:
Am just wondering something could it be the AVR-GCC and WInavr are not supporting these directives in sprintf

I will have to leave that one to the pros, but here is an alternative that does not involve the use of floating point math.
Off the top of my head and untested!

long VOLtage = (ADCvalue * 476) / 1024;

char WX[20];
itoa(VOLtage, WX, 10);

lcd_gotoxy(10,1);           
lcd_puts("V=");
lcd_putc(WX[0]);
lcd_putc('.');
lcd_putc(WX[1]);
lcd_putc(WX[2]); 

I have to hand it to you, untested, it works like that after testing here. Amazing that a a different way of looking at it solves it in an instant. Knowledge is power.

Thanks a lot for your time to look. Now I can know for sure what I am testing is right or not and move forward with the rest of it.

I would buy you a beer if it was so possible.

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

I think there are 3 versions of which printf to use in the link params in the makefile, and the default is 'dont link in the floating point print stuff', so you need to uncomment that one and comment the one that is uncommented out. The imagecraft compiler has a check box in the compile options dialog for this.

Imagecraft compiler user

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

Hi Guys,

Finally, a result.

Attached photo showing the figures.

Its a little flickery between the last digit but will add some stabilization to smooth it all out. Checked the results with a precision temp sensor and am about a little out but that should be improvable with tweaking.

Am getting 0.01 changes by blowing on the thermistor.

Attachment(s): 

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

Glad to hear you got it working :D

Regarding the tweaking, I am still rather curious about the 4.76V reference. I think part of your OP (and other questions) have not been answered fully.

Personally I would try to use the internal band gap reference if at all possible and use a look-up table (LUT) rather than all that foating point stuff (not sure if that was what Bob hinted at earlier)
I have been involved in a similar project myself, not thermistors but thermocouples, and had success with this approach as compared to a calibrated (testo) digital thermometer at different ambient temps.

Best of luck with your endeavours.

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

maximax wrote:
Glad to hear you got it working :D

Regarding the tweaking, I am still rather curious about the 4.76V reference. I think part of your OP (and other questions) have not been answered fully.

Personally I would try to use the internal band gap reference if at all possible and use a look-up table (LUT) rather than all that foating point stuff (not sure if that was what Bob hinted at earlier)
I have been involved in a similar project myself, not thermistors but thermocouples, and had success with this approach as compared to a calibrated (testo) digital thermometer at different ambient temps.

Best of luck with your endeavours.

What I wanted to do was get to this point then improve something that is kind of functional. Its kind of now so can be improved. The 4.76v was DMM measured to put into the calculations. I understand there is some way to measure the VCC of an atmel so will look at that soon so it can keep itself upto date.

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

I don't understand the resistance calculation. Seems like you have the voltage across the thermistor. I can't see the place where the current is calculated, or where the resistance is calculated. The steinhart equation uses the resistance of the thermistor, right? Step one is calc the resistance?

Imagecraft compiler user