Skip to content

Quickstart

Write one function against the interfaces, then give it ESP32 objects: an LED pin and an I2C sensor.

1. Add the component

Clone the repository into your ESP-IDF project and list it as an extra component directory (details in Integration):

git submodule add https://github.com/hardfoc/hf-internal-interface-wrap.git components/hf-internal-interface-wrap

The component needs ESP-IDF v5.5 or later (the TWAI node API that EspCan uses) and C++20.

2. Write portable code

The function below takes the interfaces, not ESP32 classes, so it builds unchanged for STM32 or a host test. It is compiled by the host build from tests/docs/quickstart.cpp.

#include "BaseGpio.h"
#include "BaseI2c.h"

// Reads a sensor's WHO_AM_I register and lights an LED when it matches.
// Nothing here knows which MCU it runs on: pass in EspGpio / EspI2cDevice,
// StmGpio / StmI2cDevice, or a test double.
bool check_sensor(BaseI2c& sensor, BaseGpio& led) {
  constexpr hf_u8_t kWhoAmIReg = 0x0F;
  constexpr hf_u8_t kExpectedId = 0x6C;

  if (!sensor.EnsureInitialized() || !led.EnsureInitialized()) {
    return false; // configures the device and the pin; a no-op the second time
  }
  hf_u8_t id = 0;
  if (!sensor.ReadRegister(kWhoAmIReg, id)) { // write the register address, read one byte
    return false;                             // no ACK: wiring, address or pull-ups
  }
  const bool ok = (id == kExpectedId);
  const hf_gpio_err_t err = ok ? led.SetActive() : led.SetInactive(); // polarity-aware
  return ok && err == hf_gpio_err_t::GPIO_SUCCESS;
}

3. Create the ESP32 objects

main.cpp
#include "mcu/esp32/EspGpio.h"
#include "mcu/esp32/EspI2c.h"

extern "C" void app_main() {
  EspGpio led(8, hf_gpio_direction_t::HF_GPIO_DIRECTION_OUTPUT);   // GPIO8, active high

  hf_i2c_master_bus_config_t bus_cfg;                              // I2C_NUM_0, internal pull-ups
  bus_cfg.sda_io_num = 6;
  bus_cfg.scl_io_num = 7;
  EspI2cBus bus(bus_cfg);
  if (!bus.Initialize()) {
    return;                                                        // pins or port already in use
  }

  hf_i2c_device_config_t dev_cfg;
  dev_cfg.device_address = 0x6A;                                   // 7-bit address
  dev_cfg.scl_speed_hz = 400000;
  BaseI2c* sensor = bus.GetDevice(bus.CreateDevice(dev_cfg));

  if (sensor == nullptr || !check_sensor(*sensor, led)) {
    // wrong address, no pull-ups, or the sensor did not answer
  }
}

Construction only stores the configuration. The bus is set up by bus.Initialize(), which must come before CreateDevice(); the pin and the device are set up by the EnsureInitialized() calls in check_sensor().

What each step does

Step What happens
EspGpio led(...) Stores pin, direction and polarity. No register is written yet
EspI2cBus bus(cfg) Stores the bus settings: port, pins, glitch filter, pull-ups
bus.Initialize() Creates the ESP-IDF I2C master bus
CreateDevice(dev_cfg) Adds a device at that address and speed and returns its index, or -1 (bus not initialised, address in use)
GetDevice(index) Returns the device as a BaseI2c* (nullptr for a bad index)
EnsureInitialized() Configures the pin, adds the device to the ESP-IDF bus; does nothing the second time
ReadRegister(reg, id) WriteRead() of one address byte and one data byte
SetActive() Drives the pin to its active level (high here, low for an active-low pin)

If it does not work

Symptom Check
CreateDevice() returns -1 bus.Initialize() was not called or failed, or the address is already on the bus
ReadRegister() returns false The 7-bit address (not shifted), SDA / SCL pins swapped, external pull-ups on a long bus
The LED never lights The pin number and HF_GPIO_ACTIVE_LOW for an LED wired to 3.3 V
Link errors for Esp* symbols The component directory is not in EXTRA_COMPONENT_DIRS
#error "No MCU selected!" A non-ESP-IDF build must define one HF_TARGET_MCU_* (see Integration)

Next: Architecture, or the API guide for the other interfaces.