Thermistor APIs

The Thermistor APIs provide a set of functions to read the temperature from a thermistor. The thermistor stores the relevant information (from the datasheet) such as the B value, the nominal resistance, circuit configuration (upper or lower part of the voltage divider), the fixed resistor value, and the supply voltage.

It uses these data to compute the measured resistance of the thermistor, from which the temperature can then be calculated using the Steinhart-Hart equation. See the Wikipedia article for more information.

Using the Steinhart-Hart equation, the temperature can be calculated using the following formula:

\[\begin{split}T = \frac{1}{\frac{1}{T_0} + \frac{1}{B} \\ln \frac{R}{R_0}}\end{split}\]

where:

  • \(T\) is the temperature in Kelvin

  • \(T_0\) is the nominal temperature in Kelvin (e.g. 298.15 K, 25 °C)

  • \(B\) is the B (beta) value of the thermistor (e.g. 3950 K)

  • \(R\) is the resistance of the thermistor, measured in Ohm

  • \(R_0\) is the nominal resistance of the thermistor (e.g. 10000 Ohm at 25 °C)

Note: the thermistor component assumes that the thermistor is used within a voltage divider circuit, with a fixed resistor. The component can be configured so that either the thermistor or the fixed resistor is the upper part of the voltage divider.

API Reference

Header File

Classes

class Thermistor : public espp::BaseComponent

Thermistor class.

Reads voltage from a thermistor and converts it to temperature using the Steinhart-Hart equation. This class is designed to be used with a NTC (negative temperature coefficient) thermistor in a voltage divider configuration.

Validation Example

    // create a lambda function for getting the voltage from the thermistor
    // this is just a simple voltage divider
    auto compute_voltage = [](float r1, float r2 = 10000, float v_in = 3300.0) -> float {
      return v_in * (r2 / (r1 + r2));
    };

    // create a table of datasheet info for the thermistor
    // this is used to calculate the temperature
    // the first value is the temperature in C
    // the second value is the resistance scale factor (R/R0)
    // This data is from the datasheet for the thermistor
    // https://product.tdk.com/system/files/dam/doc/product/sensor/ntc/chip-ntc-thermistor/data_sheet/50/db/ntc/ntc_smd_standard_series_0402.pdf
    // we chose a 10k NTC, which corresponds to the 8502 series column on
    // page 5 of the datasheet
    auto datasheet_info = std::vector<std::tuple<float, float>>{
        {0, 3.2657},  {10, 1.9907}, {20, 1.2494}, {25, 1},        {30, .80552},
        {40, .53229}, {50, .35981}, {75, .14763}, {100, .067488},
    };

    int i = 0;
    // create a lambda function for getting the voltage from the thermistor
    // using the datasheet info, this will return the voltage for the
    // current temperature (based on the index)
    auto get_voltage = [&compute_voltage, &i, &datasheet_info]() -> float {
      auto [temp, r_scale] = datasheet_info[i];
      return compute_voltage(r_scale * 10000);
    };

    // create a thermistor object
    espp::Thermistor thermistor({.divider_config = espp::Thermistor::ResistorDividerConfig::UPPER,
                                 .beta = 3940, // 25/50C beta since we're not planning on hot temps
                                 .nominal_resistance_ohms = 10000,
                                 .fixed_resistance_ohms = 10000,
                                 .supply_mv = 3300,
                                 .read_mv = get_voltage,
                                 .log_level = espp::Logger::Verbosity::INFO});

    // see how bad the error is
    for (i = 0; i < datasheet_info.size(); i++) {
      auto [actual_celsius, r_scale] = datasheet_info[i];
      auto actual_kelvin = actual_celsius + 273.15;
      // read the temperature
      auto kelvin = thermistor.get_kelvin();
      auto celsius = thermistor.get_celsius();
      // use kelvin for calculating error so we don't have to worry about division by zero
      auto error = std::abs(kelvin - actual_kelvin);
      logger.info("measured voltage: {:.2f} mV, calculated resistance: {:.2f} Ohm, calculated "
                  "temp: {:.2f} C, actual: {:.2f} C; error: {:.2f} C",
                  get_voltage(), thermistor.get_resistance(), celsius, actual_celsius, error);
    }

ADC Example

  // create a continuous ADC which will sample and filter the thermistor
  // voltage on ADC1 channel 7
  std::vector<espp::AdcConfig> channels{
      {.unit = ADC_UNIT_1, .channel = ADC_CHANNEL_7, .attenuation = ADC_ATTEN_DB_11}};
  // this initailizes the DMA and filter task for the continuous adc
  espp::ContinuousAdc adc(
      {.sample_rate_hz = 20 * 1000,
       .channels = channels,
       .convert_mode = ADC_CONV_SINGLE_UNIT_1, // or BOTH_UNIT, ALTER_UNIT, SINGLE_UNIT_1
       .window_size_bytes = 1024,
       .log_level = espp::Logger::Verbosity::WARN});
  adc.start();

  // make a lambda function for getting the latest voltage from the adc
  auto get_voltage = [&adc, &channels]() -> float {
    auto maybe_mv = adc.get_mv(channels[0]);
    if (maybe_mv.has_value()) {
      return maybe_mv.value();
    } else {
      return 0;
    }
  };

  // create a thermistor object (based on the datasheet from
  // https://product.tdk.com/system/files/dam/doc/product/sensor/ntc/chip-ntc-thermistor/catalog/tpd_commercial_ntc-thermistor_ntcg_en.pdf
  // From the table (page 7):
  // clang-format off
  // Part Number.    | R25(Ω) | Tolerance | B25/50(Κ) | B25/75(Κ) | B25/85(Κ) | B25/100(K) | Current (mA) | Operating Temp Range
  // NTCG103JF103FT1 | 10,000 |   +/–1%   | 3380      | 3422      | 3435      | 3453 +/–1% | 0.31         | –40 to 152
  // clang-format on
  espp::Thermistor thermistor({.divider_config = espp::Thermistor::ResistorDividerConfig::UPPER,
                               .beta = 3380,
                               .nominal_resistance_ohms = 10000,
                               .fixed_resistance_ohms = 10000,
                               .supply_mv = 3300,
                               .read_mv = get_voltage,
                               .log_level = espp::Logger::Verbosity::INFO});

  fmt::print("% time(s), temp(C)\n");
  auto task_fn = [&thermistor](std::mutex &m, std::condition_variable &cv) {
    static auto start = std::chrono::high_resolution_clock::now();
    auto now = std::chrono::high_resolution_clock::now();
    float elapsed = std::chrono::duration<float>(now - start).count();

    // read the temperature
    auto temp = thermistor.get_celsius();

    // print out the temperature
    fmt::print("{:.3f}, {}\n", elapsed, temp);

    // NOTE: sleeping in this way allows the sleep to exit early when the
    // task is being stopped / destroyed
    {
      std::unique_lock<std::mutex> lk(m);
      cv.wait_until(lk, now + 10ms);
    }
    // don't want to stop the task
    return false;
  };
  auto task = espp::Task({.callback = task_fn,
                          .task_config = {.name = "Read Thermistor"},
                          .log_level = espp::Logger::Verbosity::INFO});
  task.start();

Public Types

enum class ResistorDividerConfig

Enum for resistor divider configuration.

Values:

enumerator LOWER

Thermistor is the lower resistor.

enumerator UPPER

Thermistor is the upper resistor.

typedef std::function<float(void)> read_mv_fn

Function type for reading voltage.

Return

Voltage in millivolts

Public Functions

inline explicit Thermistor(const Config &config)

Constructor.

Parameters

config – Configuration struct

inline float get_resistance()

Get resistance of the thermistor.

Reads the voltage from the thermistor and converts it to resistance

Returns

Resistance of the thermistor in ohms

inline float get_kelvin()

Get the temperature in Kelvin.

Reads the voltage from the thermistor and converts it to temperature using the Steinhart-Hart equation.

Returns

Temperature in Kelvin

inline float get_celsius()

Get the temperature in Celsius.

Reads the voltage from the thermistor and converts it to temperature using the Steinhart-Hart equation. The temperature is converted from Kelvin to Celsius.

See also

get_kelvin()

Returns

Temperature in Celsius

inline float get_fahrenheit()

Get the temperature in Fahrenheit.

Reads the voltage from the thermistor and converts it to temperature using the Steinhart-Hart equation. The temperature is converted from Kelvin to Fahrenheit.

See also

get_kelvin()

Returns

Temperature in Fahrenheit

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters

rate_limit – The rate limit to use for the logger

struct Config

Configuration struct for Thermistor.

Public Members

ResistorDividerConfig divider_config

Resistor divider configuration.

float beta

Beta coefficient of the thermistor at 25C, e.g. 3950.

float nominal_resistance_ohms

Resistance of the thermistor (in ohms) at 25C, e.g. 10000.

float fixed_resistance_ohms

Resistance of the fixed resistor in the voltage divider (in ohms), e.g. 10000

float supply_mv

Supply voltage of the voltage divider (in mv), e.g. 3300.

read_mv_fn read_mv

Function for reading voltage.

Logger::Verbosity log_level = Logger::Verbosity::WARN

Log level for this class.