Skip to content

API guide

How to do the common jobs with the driver: construct, initialise, read, average and handle errors.

Every snippet on this page is compiled by the host CI from tests/docs/api_tasks.cpp, with Sensor standing for hf::mcp9700::Mcp9700Thermistor<MyAdc>. The API reference lists every member.

The shape of the API

One Mcp9700Thermistor<AdcType> object per sensor, in namespace hf::mcp9700.

Member What it does
Constructor Stores the adapter, channel and coefficients; no ADC access
Initialize() Checks the arguments, calls the adapter's EnsureInitialized()
ReadTemperatureCelsius(float*) One ADC read, converted to °C and checked against the sensor's range
ReadVoltage(float*) One ADC read, the raw VOUT in volts: no conversion, no range check
SetSpecifiedRange(min, max, margin) The temperature range a reading must fall in (default −40 °C to +125 °C, 8 °C margin)
LastError() Why the last call that can fail failed (hf::mcp9700::Error)
Deinitialize() Marks the object uninitialised; reads fail until Initialize()
VoltsAtZeroC() The offset in use
VoltsPerCelsius() The slope in use
HasValidCoefficients() true when the offset is finite and the slope is finite and above zero
GetMcp9700DriverVersion() The version string (free function)

Every call is noexcept and never throws. The calls that can fail return bool: check it.

Construct and initialise

Sensor board(&adc, 0);                          // MCP9700 / 9700A / 9700B: datasheet typicals
Sensor trimmed(&adc, 1, 0.4962F, 0.01003F);     // per-unit coefficients from a calibration
return board.Initialize() && trimmed.Initialize();

The two coefficient arguments are optional. Leave them out for the MCP9700, MCP9700A and MCP9700B datasheet typicals; pass your own after a calibration (Configuration). The channel is passed to your adapter unchanged: it means whatever your adapter says it means.

Initialize() returns false when the adapter pointer is null, when the coefficients are unusable (v_per_c zero, negative, NaN or infinite, or v_zero_c NaN or infinite), or when EnsureInitialized() returns false. Several sensors can share one adapter; each Initialize() calls EnsureInitialized() again, so make it safe to call twice.

Read the temperature

float celsius = 0.0F;
if (!sensor.ReadTemperatureCelsius(&celsius)) {
  return false;                                 // celsius is unchanged on failure
}
std::printf("%.2f C (V0 = %.4f V, Tc = %.5f V/C)\n", static_cast<double>(celsius),
            static_cast<double>(sensor.VoltsAtZeroC()), static_cast<double>(sensor.VoltsPerCelsius()));
return true;

Each call is one ReadChannelV() call. The driver keeps no history and no cache.

Average several reads

constexpr int kSamples = 16;
float sum = 0.0F;
for (int i = 0; i < kSamples; ++i) {
  float t = 0.0F;
  if (!sensor.ReadTemperatureCelsius(&t)) {
    return false;
  }
  sum += t;
}
*celsius = sum / static_cast<float>(kSamples);
return true;

The sensor's thermal response is about a second (Hardware), so averaging a burst of reads filters ADC and supply noise without hiding real temperature changes.

Handle errors

float t = 0.0F;
if (!sensor.ReadTemperatureCelsius(&t)) {
  switch (sensor.LastError()) {
    case hf::mcp9700::Error::kOutOfRange: {     // open, grounded or shorted input, or wrong ADC scale
      float volts = 0.0F;
      if (sensor.ReadVoltage(&volts)) {         // the raw VOUT, unconverted and unchecked
        std::printf("VOUT %.3f V is outside the sensor's range\n", static_cast<double>(volts));
      }
      break;
    }
    case hf::mcp9700::Error::kAdcError:         // ReadChannelV() returned non-zero
      std::printf("ADC read failed\n");
      break;
    default:                                    // not initialised or null output
      break;
  }
  return false;                                 // t is unchanged on failure
}
*celsius = t;
return true;
LastError() Meaning
Error::kInvalidArgument Null adapter, null output pointer, or an invalid range passed to SetSpecifiedRange()
Error::kInvalidCoefficients Initialize(): the slope is not a finite positive number, or the offset is not finite
Error::kAdcError Your EnsureInitialized() returned false or your ReadChannelV() returned non-zero
Error::kNotInitialized Read before a successful Initialize(), or after Deinitialize()
Error::kOutOfRange The temperature is outside the specified range plus the margin, or not a number
Error::kNone The last call succeeded

kOutOfRange catches the faults a plain conversion would report as a temperature: VOUT at 0 V (open wire, grounded input or unpowered sensor, which would read −50 °C), VOUT near the supply, or an adapter that returns millivolts. A working MCP9700 outputs 100 mV to 1.75 V over −40 °C to +125 °C (datasheet Section 3.2); with the 8 °C margin the driver accepts 20 mV to 1.83 V. A floating input that happens to settle inside that window cannot be told from a temperature. On a failed read the output value is not written. ReadVoltage() returns the raw VOUT without the range check, to see what the pin reads.

Set the plausible range

// High-temperature MCP9700 / MCP9700B (SOT-23-3, SC70-5): specified to +150 C. Keep the 8 C margin.
return sensor.SetSpecifiedRange(-40.0F, 150.0F);

The default range is the MCP9700 family's specified range. Widen it to +150 °C for the high-temperature parts, narrow it to −10 °C for the MCP9701 (Configuration). The margin (default kDefaultRangeMarginCelsius, 8 °C) covers the sensor's worst-case accuracy limits (−4 °C / +6 °C for the MCP9700) and some ADC error.

Re-initialise and read the version

sensor.Deinitialize();                          // reads now fail; the ADC itself stays as it is
if (!sensor.Initialize()) {                     // calls EnsureInitialized() again
  std::printf("ADC did not come back\n");
}
std::printf("driver %s\n", GetMcp9700DriverVersion());

Deinitialize() only changes the driver's state; shutting down the ADC is your adapter's job.

Next

Configuration for calibration; Porting for the adapter contract.