Skip to content

API guide

How to do the common jobs with the driver: configure, read, smooth, calibrate and handle errors.

Every snippet on this page is compiled by the host CI from tests/docs/api_tasks.cpp, with Ntc standing for NtcThermistor<MyAdc> and MyAdc the port from Porting. The API reference lists every member.

The shape of the API

One NtcThermistor<AdcType> object per thermistor, holding an ntc_config_t and a pointer to your ADC port. Several objects can share one port, each on its own adc_channel.

Group Calls
Lifecycle Initialize(), Deinitialize(), IsInitialized()
Read ReadTemperatureCelsius(), ReadTemperatureFahrenheit(), ReadTemperatureKelvin(), ReadTemperature()
Raw values GetResistance(), GetVoltage(), GetRawAdcValue()
Configure SetConfiguration(), GetConfiguration(), SetAdcChannel(), SetVoltageDivider(), SetReferenceVoltage(), SetBetaValue(), SetConversionMethod()
Smooth SetSamplingParameters(), SetFiltering()
Calibrate Calibrate(), SetCalibrationOffset(), GetCalibrationOffset(), ResetCalibration()
Utilities CelsiusToFahrenheit() and the other unit conversions, GetErrorString(), GetTypeString(), GetDriverVersion()

Initialize() returns bool; every other call that can fail returns an NtcError and writes its result through a pointer. Nothing throws or allocates.

Configure

From a part type, then adjust the board-specific values:

Ntc ntc(NtcType::NtcG163Jf103Ft1S, &adc);      // R25 = 10 kOhm, B = 3435 K, defaults for the rest
if (ntc.SetAdcChannel(2) != NtcError::Success ||
    ntc.SetVoltageDivider(10000.0F) != NtcError::Success ||   // series resistor (ohms)
    ntc.SetReferenceVoltage(3.3F) != NtcError::Success) {     // divider supply (V)
  return false;
}
return ntc.Initialize();

Or from a full configuration, for any thermistor:

ntc_config_t cfg = GetDefaultNtcConfig();
cfg.type = NtcType::Custom;
cfg.resistance_at_25c = 100000.0F;   // a 100 kOhm part
cfg.beta_value = 4250.0F;            // its B25/85 (or B25/50) from the datasheet
cfg.series_resistance = 100000.0F;   // match R25 for the best resolution around 25 C
cfg.reference_voltage = 3.3F;
cfg.adc_channel = 0;
cfg.min_temperature = -20.0F;        // readings outside this window return TemperatureOutOfRange
cfg.max_temperature = 100.0F;

Ntc ntc(cfg, &adc);
return ntc.Initialize();             // false: invalid config, ADC not up, or no such channel

Initialize() returns false when a value is invalid (R25, the supplies or the series resistor not positive; B outside 1000 K to 5000 K; sample_count zero; min_temperature not below max_temperature; filter_alpha outside 0 to 1 with filtering on), when the port's EnsureInitialized() fails, or when IsChannelAvailable(adc_channel) is false. SetConfiguration() checks the same values and returns InvalidParameter. Configuration lists every field.

Read

ntc_reading_t r{};
if (ntc.ReadTemperature(&r) == NtcError::Success && r.is_valid) {
  std::printf("%.2f C  %.2f F  %.0f ohm  %.3f V  raw %lu\n", r.temperature_celsius,
              r.temperature_fahrenheit, r.resistance_ohms, r.voltage_volts,
              static_cast<unsigned long>(r.adc_raw_value));
}

float ohms = 0.0F;
if (ntc.GetResistance(&ohms) == NtcError::Success) {
  std::printf("thermistor: %.0f ohm\n", ohms);
}

ReadTemperatureCelsius() is one conversion. ReadTemperature() fills every unit plus the resistance, voltage and raw count, but reads the ADC again for each of those: with averaging on, it costs four times the samples. timestamp_us is always 0 and accuracy_celsius always 0.5; fill in your own if you need them.

Smooth

ntc.SetSamplingParameters(16, 0);     // average 16 ADC reads per reading
ntc.SetFiltering(true, 0.2F);         // then an EMA: new = 0.2 * reading + 0.8 * previous
Setting Effect Cost
sample_count Averages that many ADC reads; failed reads are skipped sample_count reads per call
sample_delay_ms A wait between samples through the port's DelayMs() (Porting); none if the port has no DelayMs() The call blocks (sample_count − 1) × sample_delay_ms
enable_filtering, filter_alpha EMA after calibration: new = α · reading + (1 − α) · previous None; the first reading seeds it

sample_delay_ms blocks the calling task for the whole wait; leave it 0 unless the samples must be spread out, and give the port a DelayMs() that sleeps rather than spins. A smaller α smooths more and responds more slowly; changing the filter or the configuration resets it.

Calibrate

// With the sensor at a known temperature (a reference thermometer reads 23.4 C):
ntc.ResetCalibration();
if (ntc.Calibrate(23.4F) == NtcError::Success) {
  float offset = 0.0F;
  ntc.GetCalibrationOffset(&offset);  // store it; restore with SetCalibrationOffset(offset)
  std::printf("offset %.2f C\n", offset);
}

Calibrate(t) reads the sensor, with the current offset applied, and adds t − reading to the offset, so the sensor then reads t whatever offset was set before. ResetCalibration() first is optional; it starts from zero, as above. The offset is a constant: it corrects R25 tolerance and divider errors well, and the Beta equation's error only near the calibration temperature (Conversion methods). The offset lives in RAM; store it yourself.

Handle errors

float c = 0.0F;
switch (const NtcError err = ntc.ReadTemperatureCelsius(&c); err) {
  case NtcError::Success:
    break;
  case NtcError::ConversionFailed:       // node at 0 V or at the supply: shorted or open sensor
  case NtcError::TemperatureOutOfRange:  // converted, but outside min/max_temperature
    std::printf("sensor fault: %s\n", Ntc::GetErrorString(err));
    break;
  default:                               // AdcReadFailed, Timeout, HardwareFault, NotInitialized ...
    std::printf("ADC: %s\n", Ntc::GetErrorString(err));
    break;
}
NtcError Meaning Where to look
NotInitialized Initialize() not called or failed Your port's EnsureInitialized()
NullPointer A null output pointer, or no ADC port The call site
AdcReadFailed, Timeout, HardwareFault Your port returned ReadFailed, Timeout or HardwareError Your port
InvalidParameter A setter or SetConfiguration() rejected a value; or InvalidChannel from the port The value
ConversionFailed The node at 0 V or at V_REF, or a resistance outside 0.1 Ω to 1 MΩ Wiring: shorted or open sensor
TemperatureOutOfRange Converted, but outside min_temperature to max_temperature The range, or a real over-temperature

With averaging on, a single failed sample is skipped; the call fails with AdcReadFailed only when every sample fails. GetErrorString() returns a short text for each code.

Next

Configuration lists every field; Troubleshooting maps symptoms to causes.