Skip to content

Porting

Run the driver on a new MCU by writing one class: the ADC adapter.

The driver never calls your SDK directly. It calls two methods on the class you pass as the AdcType template parameter (documented on Mcp9700Thermistor in inc/mcp9700_thermistor.hpp). There is no base class: the calls are resolved at compile time, so there are no virtual functions, no vtable and nothing to allocate.

Layer diagram: the application calls the hardware-agnostic MCP9700 driver, which relies on the ADC adapter contract, which the platform adapter you write fulfils, which reads the MCU's ADC connected to the MCP9700's analog output.Layer diagram: the application calls the hardware-agnostic MCP9700 driver, which relies on the ADC adapter contract, which the platform adapter you write fulfils, which reads the MCU's ADC connected to the MCP9700's analog output.
Three layers, one of them yours
Figure description and sources

From top to bottom. Your application talks to one Mcp9700Thermistor object per sensor, whose public calls (listed from inc/mcp9700_thermistor.hpp) initialise the ADC and return degrees Celsius. The core turns one ADC voltage into a temperature with (V - v_zero_c) / v_per_c. The ADC adapter contract is the template parameter: any class with the two methods named in the header's documentation, checked at compile time. The platform port is the only code you write: that class, which brings your ADC up and returns the voltage at the sensor's VOUT pin. Below it is the hardware.

Drawn from: inc/mcp9700_thermistor.hpp Mcp9700Thermistor public calls; inc/mcp9700_thermistor.hpp AdcType contract; figure module docs/diagrams/figures/driver-layers.py.

What you implement

Both are noexcept; the adapter below shows the exact signatures.

Method Contract
EnsureInitialized() Bring the ADC up; return true when it can read. Called by every Initialize()
ReadChannelV() One reading of channel, in volts at the sensor pin, into *voltage_v; return 0 on success, anything else on failure

ReadChannelV() returns an int where 0 is success, so an adapter can return the value of an error enum whose success value is 0. The driver only tests it against zero.

The adapter

my_adc.hpp
class MyAdc {
public:
  // Bring the ADC up. The driver calls this from Initialize(); return true when it is ready.
  bool EnsureInitialized() noexcept {
    if (!ready_) {
      ready_ = platform_adc_init();
    }
    return ready_;
  }

  // Volts at the MCP9700 VOUT pin. Return 0 on success, any other value on failure.
  int ReadChannelV(uint8_t channel, float* voltage_v) noexcept {
    int32_t mv = 0;
    if (!ready_ || !platform_adc_read_mv(channel, &mv)) {
      return 1;
    }
    *voltage_v = static_cast<float>(mv) / 1000.0F;
    return 0;
  }

private:
  bool ready_ = false;
};

Rules the adapter must follow:

  • Volts, at the pin. Not millivolts, not raw codes. Undo any divider, attenuator or amplifier gain.
  • Calibrated. Apply your ADC's calibration (offset, gain, curve) before returning.
  • Idempotent initialisation. EnsureInitialized() may be called once per sensor and again after Deinitialize(); set the ADC up only the first time.
  • Fail loudly. Return non-zero when the conversion fails or the channel is unknown; the driver then leaves the caller's value untouched. Do not write *voltage_v on failure.
  • One channel, one sensor. The channel number is yours to define: an ADC channel, a multiplexer input or an index into a table.

Reference implementation

The ESP32-C6 example's Esp32Mcp9700Adc in mcp9700_esp32c6_example.cpp is a complete adapter over ESP-IDF adc_oneshot: ADC_UNIT_1, ADC_CHANNEL_0 (GPIO0), 12-bit, with curve-fitting calibration when the chip supports it. Start from it for any ESP-IDF target.

Check your adapter

  1. Build it: the adapter on this page is compiled by the host CI from tests/docs/my_adc.hpp, with the driver instantiated over it.
  2. Measure VOUT with a multimeter and compare it with what ReadChannelV() returns. They should agree within your ADC's error.
  3. At room temperature, ReadTemperatureCelsius() should report roughly +20 °C to +30 °C (VOUT about 0.7 V to 0.8 V).
  4. Touch the sensor: the reading should rise within a few seconds.

Threads

The driver is not thread-safe. Give each sensor one owner task, or guard every call on a driver object (and its adapter) with one mutex. Sensors that share an ADC share its lock too.

Next

Testing and CI shows how the adapter and driver are tested without hardware.