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
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:
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_SPIandSensorBuilder.There is also a FIXME in the codebase that appears to describe the similar problem:
Local patch
For now, I am guarding the boot-time I2C calls by checking whether valid I2C pins are configured:
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_SENSORSfrom the configured sensor list.The early startup path could then be gated explicitly:
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:I currently handle this locally by adding
DIRECT_SPI_PINS(...)and passing explicit SCK, MISO and MOSI values intoDirectSPIInterface.Would it make sense to formalize the shared SPI bus pins as board-level definitions, similar to the existing I2C definitions?
Unused pins could default to
255, asPIN_IMU_SDAandPIN_IMU_SCLalready 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
accessInterfacefield ofsensorDescEntry(), 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