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.