Skip to content

SPI-only ESP32-C3 halts on I2C initialization during boot #537

Description

@Feather-P

Running an LSM6DSV over SPI on an ESP32-C3. No I2C sensors are defined in the build.

Issue

The board hangs during boot. After tracing the startup logs, I found that it gets stuck in the global I2C reset/initialization path in setup():

  • I2CSCAN::clearBus()
  • Wire.begin()

From the nearby comment, this code appears to be a workaround for the ESP32-C3 sometimes having its only I2C interface already open for an unknown reason:

// For some unknown reason the I2C seem to be open on ESP32-C3 by default. Let's
// just close it before opening it again. (The ESP32-C3 only has 1 I2C.)

Since this board is not wired for I2C, the initialization path fails or hangs. This seems inconsistent with the bus-aware configuration already supported later by DIRECT_SPI and SensorBuilder.

There is also a FIXME in the codebase that appears to describe the similar problem:

FIXME fix the I2C Scanner so it use the sensor list and not be called when no I2C sensor

Local patch

For now, I am guarding the boot-time I2C calls by checking whether valid I2C pins are configured:

if (PIN_IMU_SDA != 255 && PIN_IMU_SCL != 255) {
    // I2C clear/init logic
}

I have had to add similar checks around several I2C-related calls, not just I2CSCAN::clearBus().

This gets the board through startup, but repeating the same pin check in multiple places feels more like a workaround than a proper fix.

Fix, maybe

I think the startup logic should derive its behavior from the generated sensor configuration rather than assuming that I2C is always present.

1. Gate the I2C startup path

The preprocessor could generate a simple flag such as HAS_I2C_SENSORS from the configured sensor list.

The early startup path could then be gated explicitly:

#if HAS_I2C_SENSORS
    I2CSCAN::clearBus(PIN_IMU_SDA, PIN_IMU_SCL);
    Wire.begin(PIN_IMU_SDA, PIN_IMU_SCL);
#endif

The exact implementation does not necessarily need to use a preprocessor flag, but the decision should ideally come from the same sensor topology represented by SENSOR_DESC_LIST.

2. Make the shared SPI configurable

On the ESP32-C3, routing SPI to non-default pins requires calling SPIClass::begin() with explicit pins:

SPI.begin(sck, miso, mosi, -1);

I currently handle this locally by adding DIRECT_SPI_PINS(...) and passing explicit SCK, MISO and MOSI values into DirectSPIInterface.

Would it make sense to formalize the shared SPI bus pins as board-level definitions, similar to the existing I2C definitions?

PIN_IMU_SCK
PIN_IMU_MISO
PIN_IMU_MOSI

Unused pins could default to 255, as PIN_IMU_SDA and PIN_IMU_SCL already do.

SCK, MISO and MOSI belong to the shared SPI bus, while chip select is specific to each sensor. The current model already passes the CS pin through the accessInterface field of sensorDescEntry(), which also allows different SPI sensors to use different CS pins.

I have the I2C guards and explicit SPI pin handling working locally. 0v0

I am not strongly attached to a particular implementation. If this direction fits the intended architecture, I can clean up the changes and submit a PR. :3

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions