Quickstart¶
Read a temperature from your MCU: wire a divider, write a small ADC port class, call two driver functions.
1. Wire it¶
The thermistor sits between the ADC node and ground; the series resistor between the supply and the node.
| From | To | Notes |
|---|---|---|
| 3.3 V | R_SERIES | 10 kΩ, 1 % or better; the supply value goes in reference_voltage |
| R_SERIES | ADC input | The divider node |
| ADC input | NTC | NTCG163JF103FT1S or any 10 kΩ NTC |
| NTC | GND | Same ground as the ADC |
Keep the node voltage inside the ADC's input range. With 3.3 V and 10 kΩ / 10 kΩ it is 1.65 V at 25 °C and moves toward 0 V as the sensor heats. The Hardware page covers resistor choice, filtering and self-heating.
2. Add the driver¶
Add the repository as a submodule and link the hf_ntc_thermistor CMake target.
Installation covers CMake and ESP-IDF.
git submodule add https://github.com/hardfoc/hf-ntc-thermistor-driver.git external/hf-ntc-thermistor-driver
3. Write the port¶
The port is the only platform code. It brings the ADC up and returns a channel's voltage and raw count. Replace
the two platform_* calls with your SDK's.
class MyAdc : public ntc::AdcInterface<MyAdc> {
public:
static constexpr uint8_t kChannels = 7; // ADC1 channels 0..6 on this MCU
static constexpr uint8_t kBits = 12;
static constexpr float kFullScaleV = 3.3F; // input range at the chosen attenuation
[[nodiscard]] bool IsInitialized() const { return ready_; }
// Bring the ADC up once; later calls return true at once.
bool EnsureInitialized() {
if (!ready_) {
ready_ = platform_adc_init();
}
return ready_;
}
[[nodiscard]] bool IsChannelAvailable(uint8_t channel) const { return channel < kChannels; }
ntc::AdcError ReadChannelCount(uint8_t channel, uint32_t* count) {
uint32_t mv = 0;
return read(channel, count, &mv);
}
// The driver reads volts: return the calibrated voltage at the pin, not count * Vref / 4095.
ntc::AdcError ReadChannelV(uint8_t channel, float* voltage_v) {
uint32_t raw = 0;
uint32_t mv = 0;
const ntc::AdcError err = read(channel, &raw, &mv);
if (err == ntc::AdcError::Success) {
*voltage_v = static_cast<float>(mv) / 1000.0F;
}
return err;
}
[[nodiscard]] float GetReferenceVoltage() const { return kFullScaleV; }
[[nodiscard]] uint8_t GetResolutionBits() const { return kBits; }
// Optional: the driver waits sample_delay_ms between averaged samples through this.
void DelayMs(uint32_t ms) { platform_delay_ms(ms); }
private:
ntc::AdcError read(uint8_t channel, uint32_t* raw, uint32_t* mv) {
if (!ready_) {
return ntc::AdcError::NotInitialized;
}
if (!IsChannelAvailable(channel)) {
return ntc::AdcError::InvalidChannel;
}
if (!platform_adc_read(channel, *raw, *mv)) {
return ntc::AdcError::ReadFailed;
}
return ntc::AdcError::Success;
}
bool ready_ = false;
};
Return the calibrated voltage at the pin from ReadChannelV(): the driver computes the resistance from it.
Porting explains each method.
4. Read the temperature¶
#include "ntc_thermistor.hpp"
#include <cstdio>
int read_board_temperature(MyAdc& adc, float& celsius) {
ntc_config_t cfg = GetDefaultNtcConfig(); // NTCG163JF103FT1S: 10 kOhm at 25 C, B 3435 K
cfg.adc_channel = 2; // 1. the ADC input on the divider node
cfg.series_resistance = 10000.0F; // 2. the resistor from the supply to the node
cfg.reference_voltage = 3.3F; // 3. the supply of the divider
NtcThermistor<MyAdc> ntc(cfg, &adc);
if (!ntc.Initialize()) { // 4. ADC up, channel present, config valid
return -1;
}
const NtcError err = ntc.ReadTemperatureCelsius(&celsius); // 5. volts -> ohms -> C
if (err != NtcError::Success) {
std::printf("NTC: %s\n", NtcThermistor<MyAdc>::GetErrorString(err));
return -2;
}
return 0;
}
What each step does:
adc_channelselects the input your port reads.series_resistanceis the resistor between the supply and the node, in ohms.reference_voltageis the divider's supply voltage. The driver solves RNTC = RSERIES · V / (VREF − V) with it.Initialize()checks the configuration, brings the ADC up and checks the channel.ReadTemperatureCelsius()reads the voltage, converts it and checks the result againstmin_temperature/max_temperature(−40 °C to +125 °C by default).
This example is built by the host CI, so it compiles against the current headers.
If it does not work¶
| Symptom | First thing to check |
|---|---|
Initialize() returns false |
EnsureInitialized() and IsChannelAvailable() in your port; a zero or negative value in the config |
Temperature conversion failed |
The node reads 0 V (thermistor shorted) or the supply (thermistor open) |
| A few degrees off at every temperature | reference_voltage or series_resistance differ from the board |
| Reading moves the wrong way | Thermistor and series resistor swapped (Hardware) |
Next¶
- API guide: averaging, filtering, calibration and error handling.
- Configuration: every field of
ntc_config_t, and other thermistors. - Conversion methods: how accurate the Beta equation is for this part.