Custom Adafruit Thermistor Library

Hi All,

I'm in the process of making a custom library. This is my first crack at making libraries so I thought I would start with a code set I'm very familiar with:

My thermistors are reacting but the reading are not accruate at all. My libraries work when I put the RNOM, TNOM, BCOE, SRES parameters in the globals.h instead of feeding them through via the AC_Test.ino file ( I would rather do this for when I use this library for various other thermistors). I plan on continueing the development with this library (variables for NTC/PTC and other things) and making it public for others to use.
globals.h:

#pragma once 
#ifndef ARDUINO_H
  #include <Arduino.h>
#endif

#define tms_period  10 //Ten millisecond period
#define NSAMP       5     // Number of smaples taken for average

thermistor.h:

#ifndef thermistor_h
#define thermistor_h
#include "globals.h"
class ThermADC
{
  public:
    ThermADC(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES);
    uint16_t read();
  private:
    uint8_t pin; // analog input 
    uint16_t RNOM;//nominal value of resistance of the thermistor at 25 *C
    uint8_t TNOM;//nominal temperature for 10kΩ resistance 
    uint16_t BCOE;// Beta coeffiecient fo the thermistor 
    uint16_t SRES;// Value of R1 in the voltage divider
    void setThermistorParameters(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES);
    uint8_t getPin();
    uint16_t getRNOM();
    uint8_t getTNOM();
    uint16_t getBCOE();
    uint16_t getSRES();
};
#endif

thermistor.cpp file:

#include "thermistor.h"

ThermADC::ThermADC(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES)
{
  setThermistorParameters(pin,RNOM,TNOM,BCOE,SRES);
}

uint16_t ThermADC::read()
{
    uint8_t i;
    uint8_t samp[i];
    for (i = 0; i < NSAMP; i++)
    {
      samp[i] = analogRead(getPin());
      unsigned long current_millis_tms = millis();
      while(millis() < current_millis_tms + tms_period)
      {
      // 10 millisecond delay
      }
    }
    float  average = 0;
    for (i = 0; i < NSAMP; i++)
    {
      average += samp[i];
    }
    average /= NSAMP;
    // convert the value to resistance
    average = 1023 / average - 1;
    average = getSRES() / average;
    // Convert resistance to the temperature value 
    float steinhart;
    steinhart = average / getRNOM() ;             // (R/Ro)
    steinhart = log(steinhart);               // ln(R/Ro)
    steinhart /= getBCOE();                          // 1/B * ln(R/Ro)
    steinhart += 1.0 / (getTNOM()+ 273.15);        // + (1/To)
    steinhart = 1.0 / steinhart;              // Invert
    steinhart -= 273.15;                        // convert absolute temp to C
    float t = steinhart*(1.8) + 32;  // convert to *F
    return t;
}

void ThermADC::setThermistorParameters(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES)
{
  this->pin = pin;
  this->RNOM = RNOM;
  this->TNOM = TNOM;
  this->BCOE = BCOE;
  this->SRES = SRES;
}

uint8_t ThermADC::getPin()
{
  return this-> pin;
}

uint16_t ThermADC::getRNOM()
{
  return this-> RNOM;
}

uint8_t ThermADC::getTNOM()
{
  return this-> TNOM;
}

uint16_t ThermADC::getBCOE()
{
  return this-> BCOE;
}

uint16_t ThermADC::getSRES()
{
  return this-> SRES;
}

.ino file:

#include <thermistor.h>
//Timer Variables 
unsigned long current_millis = 0; 
//Timer Definitions 
#define sec_period  1000

//Thermistor: ThermADC [varible]([analog pin],[Thermistor nominal resistance],[Nominal Temperature],[Beta Coefficient],[Value of R1])
ThermADC t1(A0, 10000, 25, 3950, 10000);
ThermADC t2(A1, 10000, 25, 3950, 10000);

void setup() 
{
  Serial.begin(9600);
}
void loop() 
{ 
  //Timer/////////////////////////////////////////////////////////////////////////////////
  current_millis = millis(); 

  //Thermistor Readings////////////////////////////////////////////////
  float o= t1.read();     
  float r = t2.read();  

  //Print Statments

  Serial.print("Thermistor 1: "); 
  Serial.print(o);
  Serial.println(" *F");
  
  Serial.print("Thermistor 2: ");
  Serial.print(r);
  Serial.println(" *F");  
  Serial.println(); 
  while(millis() < current_millis + sec_period)
  {
    // 1 second delay
  }
}

Here is what the serial output is:

Thermistor 1: 8717.00 *F
Thermistor 2: 260.00 *F

Thermistor 1: 8717.00 *F
Thermistor 2: 260.00 *F

Thermistor 1: 8717.00 *F
Thermistor 2: 260.00 *F

Hello,

Your read method doesn't return a float but a uint16_t, so your variable t is converted from float to uint16_t (you should have a compile warning about that), that's why you see strange results

Thank you for responding, I have made changes to:
thermistor.h (changed uint16_t to float):

#ifndef thermistor_h
#define thermistor_h
#include "globals.h"
class ThermADC
{
  public:
    ThermADC(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES);
    float read();
  private:
    uint8_t pin; // analog input 
    uint16_t RNOM;//nominal value of resistance of the thermistor at 25 *C
    uint8_t TNOM;//nominal temperature for 10kΩ resistance 
    uint16_t BCOE;// Beta coeffiecient fo the thermistor 
    uint16_t SRES;// Value of R1 in the voltage divider
    void setThermistorParameters(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES);
    uint8_t getPin();
    uint16_t getRNOM();
    uint8_t getTNOM();
    uint16_t getBCOE();
    uint16_t getSRES();
};
#endif

and the thermistor.cpp (same as the header file change):

#include "thermistor.h"

ThermADC::ThermADC(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES)
{
  setThermistorParameters(pin,RNOM,TNOM,BCOE,SRES);
}

float ThermADC::read()
{
    uint8_t i;
    uint8_t samp[i];
    for (i = 0; i < NSAMP; i++)
    {
      samp[i] = analogRead(getPin());
      unsigned long current_millis_tms = millis();
      while(millis() < current_millis_tms + tms_period)
      {
      // 10 millisecond delay
      }
    }
    float  average = 0;
    for (i = 0; i < NSAMP; i++)
    {
      average += samp[i];
    }
    average /= NSAMP;
    // convert the value to resistance
    average = 1023 / average - 1;
    average = getSRES() / average;
    // Convert resistance to the temperature value 
    float steinhart;
    steinhart = average / getRNOM() ;             // (R/Ro)
    steinhart = log(steinhart);               // ln(R/Ro)
    steinhart /= getBCOE();                          // 1/B * ln(R/Ro)
    steinhart += 1.0 / (getTNOM()+ 273.15);        // + (1/To)
    steinhart = 1.0 / steinhart;              // Invert
    steinhart -= 273.15;                        // convert absolute temp to C
    float t = steinhart*(1.8) + 32;  // convert to *F
    return t;
}

void ThermADC::setThermistorParameters(uint8_t pin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES)
{
  this->pin = pin;
  this->RNOM = RNOM;
  this->TNOM = TNOM;
  this->BCOE = BCOE;
  this->SRES = SRES;
}

uint8_t ThermADC::getPin()
{
  return this-> pin;
}

uint16_t ThermADC::getRNOM()
{
  return this-> RNOM;
}

uint8_t ThermADC::getTNOM()
{
  return this-> TNOM;
}

uint16_t ThermADC::getBCOE()
{
  return this-> BCOE;
}

uint16_t ThermADC::getSRES()
{
  return this-> SRES;
}

and the result are better but still slightly off (suppose to be 70 to 74, thermistor two was on my leg so that's why that reading is slightly higher):
Thermistor 1: 134.90 *F
Thermistor 2: 141.79 *F

Thermistor 1: 134.96 *F
Thermistor 2: 141.73 *F

Thermistor 1: 134.90 *F
Thermistor 2: 141.79 *F

Thermistors are not precision devices and there will be variation between them - some of the cheap ones you can buy are probably outside of spec anyway .
The other issue is the resolution of you measurement is limited ( 1 part in 1024- in fact far less as your analog signal won’t go from 0 to full scale ) . A resistor divider with a non linear device will have different resolution at one end of its scale.
A problem often occurs when using floats , logs etc that you think you are getting a higher resolution result than the basic “1024” resolution can provide .
A simple curve fit over the range of interest may give a better result.

It’s worth looking at the basic reading coming in from the A/D to see what you are actually getting. For testing you need some known accurate temperatures ( eg sensor in a thermowell in ice water mix, don’t forget boiling pt varies with altitude too ).
Another problem is self heating from the current put through the thermostat and lead resistance .
You are up against it for accuracy from a thermistor without calibration

Useful link

Finally different technologies such as DS18b20 can give a far better result

Thank you for your reply but it has nothing to do with what I'm asking. The question I asked has to deal with building libraries, not the performance of the thermistor or code that yields the result. I've be using the math used in the .cpp file for years (with slight changes that account for self heating) and have been very happy with the result. I have just been writing it within the skecth file rather than using a library. In fact, if you read my post, I do achieve the correct values if my parameters are put into the globals.h file and not pass through from the sketch.

Hi everyone,

I have figure out the issue.
When I declared my sample array, I declared it as an uint8_t and needs to be at least uint16_t. I also made other changes in the get functions (removed them). I also went ahead and added a self heating protection piece in there (the user has the ability to use a GPIO to power voltage divider circuit only when a reading needs to be taken or have the voltage divider constantly powered- subjecting it to self heating) . I will continue to work on this library to be used for any kind of thermistor and other circuits that are commonly used (not just a voltage divider going into an analog input) and post on my github page. Here is the new code set.

.ino file:

#include <thermistor.h>
//Timer Variables 
unsigned long current_millis = 0; 
//Timer Definitions 
#define sec_period  1000

ThermADC t1
( 
  A0,     //analog input pin 
  7,     //power GPIO (will go high when taking a reading, put -1 if the top of the voltage divider is tied to supply rail)
  10000,  //Thermistor nominal resistance 
  25,     //Nominal Temperature 
  3950,   //Beta Coefficient 
  10000   //Value of R1 in the voltage divider 
); 

ThermADC t2
( 
  A1,     //analog input pin
  6,     //power GPIO (will go high when taking a reading, put -1 if the  top of the voltage divider is tied to supply rail)
  10000,  //Thermistor nominal resistance 
  25,     //Nominal Temperature 
  3950,   //Beta Coefficient 
  10000   //Value of R1 in the voltage divider
);

void setup() 
{
  Serial.begin(9600);
  analogReference(EXTERNAL);
}
void loop() 
{ 
  //Timer/////////////////////////////////////////////////////////////////////////////////
  current_millis = millis(); 

  //Thermistor Readings////////////////////////////////////////////////
  float o= t1.read();     
  float r = t2.read();  

  //Print Statments

  Serial.print("Thermistor 1: "); 
  Serial.print(o);
  Serial.println(" *F");
  
  Serial.print("Thermistor 2: ");
  Serial.print(r);
  Serial.println(" *F");  
  Serial.println(); 
  while(millis() < current_millis + sec_period)
  {
    // 1 second delay
  }
}

thermistor.h:

#ifndef thermistor_h
#define thermistor_h
#include "globals.h"
class ThermADC
{
  public:
    ThermADC(uint8_t pin, uint8_t dpin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES);
    float read();
  private:
    uint8_t pin;   // analog input 
    uint8_t dpin;  // Digital Power GPIO, helps with self-heating 
    uint16_t RNOM; //nominal value of resistance of the thermistor at 25 *C
    uint8_t TNOM;  //nominal temperature for 10kΩ resistance 
    uint16_t BCOE; // Beta coeffiecient fo the thermistor 
    uint16_t SRES; // Value of R1 in the voltage divider
    void setThermistorParameters(uint8_t pin, uint8_t dpin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES);
};
#endif

thermistor.cpp:

#include "thermistor.h"

ThermADC::ThermADC(uint8_t pin, uint8_t dpin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES)
{
  setThermistorParameters(pin,dpin,RNOM,TNOM,BCOE,SRES);
}

float ThermADC::read()
{
    uint8_t i;
    uint16_t samp[NSAMP];
    if(dpin >= 1)
    {
      digitalWrite(dpin, HIGH); 
    }
    unsigned long current_millis_tms = millis();
    while(millis() < current_millis_tms + tms_period)
    {
      // 10 millisecond delay
    } 
    for (i = 0; i < NSAMP; i++)
    { 
      samp[i] = analogRead(pin);
      current_millis_tms = millis();
      while(millis() < current_millis_tms + tms_period)
      {
      // 10 millisecond delay
      }  
    }
    if(dpin >= 1)
    {
      digitalWrite(dpin, LOW); 
    }
    float  average = 0;
    for (i = 0; i < NSAMP; i++)
    {
      average += samp[i];
    }
    average /= NSAMP;
    // convert the value to resistance
    average = 1023 / average - 1;
    average = SRES / average;
    // Convert resistance to the temperature value 
    float steinhart;
    steinhart = average / RNOM ;             // (R/Ro)
    steinhart = log(steinhart);               // ln(R/Ro)
    steinhart /= BCOE;                          // 1/B * ln(R/Ro)
    steinhart += 1.0 / (TNOM+ 273.15);        // + (1/To)
    steinhart = 1.0 / steinhart;              // Invert
    steinhart -= 273.15;                        // convert absolute temp to C
    float t = steinhart*(1.8) + 32;  // convert to *F
    return t;
}

void ThermADC::setThermistorParameters(uint8_t pin, uint8_t dpin, uint16_t RNOM, uint8_t TNOM, uint16_t BCOE, uint16_t SRES)
{
  this->pin = pin;
  this->dpin = dpin;
  this->RNOM = RNOM;
  this->TNOM = TNOM;
  this->BCOE = BCOE;
  this->SRES = SRES;
}

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