437 lines
10 KiB
Markdown
437 lines
10 KiB
Markdown
# LSM6DS External Component Bug Investigation
|
|
|
|
## Purpose
|
|
|
|
This document records the investigation and correction of an LSM6DS
|
|
external-component bug found while diagnosing recurring ESP32-C6
|
|
interrupt-watchdog resets in the RV level sensor.
|
|
|
|
It separates confirmed driver bugs, supporting diagnostic evidence,
|
|
fixes made in the `rv-lsm6ds` branch, validation results, and the
|
|
still-unproven relationship between these bugs and the
|
|
interrupt-watchdog resets.
|
|
|
|
A concise upstream bug report is included at the end.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 1. Test environment
|
|
|
|
### Hardware
|
|
|
|
- Seeed Studio XIAO ESP32-C6
|
|
- Modulino Movement / LSM6DSOXTR IMU
|
|
- I2C address: `0x6A`
|
|
- I2C SDA: GPIO22
|
|
- I2C SCL: GPIO23
|
|
|
|
### Software
|
|
|
|
- ESPHome 2026.8.2
|
|
- ESP-IDF framework
|
|
- External component fork:
|
|
`https://git.mr2web.net/mr2web/esphome-lsm6ds.git`
|
|
- Working branch: `rv-lsm6ds`
|
|
|
|
Relevant configuration:
|
|
|
|
``` yaml
|
|
lsm6ds_i2c:
|
|
id: imu
|
|
i2c_id: i2c_bus
|
|
address: 0x6A
|
|
accel_range: 2G
|
|
gyro_range: 250DPS
|
|
accel_odr: 104HZ
|
|
gyro_odr: "OFF"
|
|
update_interval: 10ms
|
|
```
|
|
|
|
The accelerometer intentionally remains at 104 Hz. High-rate sampling is
|
|
required by the RV leveling application because lower sampling
|
|
previously produced vibration/aliasing-related drift.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 2. Original investigation context
|
|
|
|
The RV level sensor experienced recurring hangs followed by ESP32-C6
|
|
interrupt-watchdog resets:
|
|
|
|
``` text
|
|
Reason: Interrupt wdt
|
|
Crashed core: 0
|
|
```
|
|
|
|
During a live failure, ESPHome API/entity updates stopped while the
|
|
device continued responding to ping. It later rebooted and reported the
|
|
watchdog reset.
|
|
|
|
The leveling calculation uses accelerometer data only. Gyroscope and
|
|
temperature data are not required for leveling.
|
|
|
|
Inspection of the external component showed that its polling `update()`
|
|
function reads `STATUS_REG` and conditionally reads/publishes gyroscope,
|
|
accelerometer and temperature data. At a 10 ms polling interval this is
|
|
a high-frequency I2C path.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 3. Adding support for ODR OFF
|
|
|
|
The C++ component already defined:
|
|
|
|
``` cpp
|
|
LSM6DS_ODR_OFF = 0x00
|
|
```
|
|
|
|
but the ESPHome configuration schema did not expose `OFF`.
|
|
|
|
This was corrected so YAML could use:
|
|
|
|
``` yaml
|
|
gyro_odr: "OFF"
|
|
```
|
|
|
|
`OFF` is quoted because an unquoted YAML value may be interpreted as
|
|
boolean false.
|
|
|
|
Commit:
|
|
|
|
``` text
|
|
41cb715d6 lsm6ds: expose OFF output data rate
|
|
```
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 4. Disabled-sensor read guards
|
|
|
|
After configuring `gyro_odr: "OFF"`, the driver continued publishing
|
|
repeated frozen gyroscope values.
|
|
|
|
A guard was added so a disabled sensor would not be read even if the
|
|
status test appeared true:
|
|
|
|
``` cpp
|
|
if (this->gyro_odr_ != LSM6DS_ODR_OFF && ...) {
|
|
// gyro read
|
|
}
|
|
|
|
if (this->accel_odr_ != LSM6DS_ODR_OFF && ...) {
|
|
// accelerometer read
|
|
}
|
|
```
|
|
|
|
Commit:
|
|
|
|
``` text
|
|
3b86faa44 lsm6ds: skip reads for disabled sensors
|
|
```
|
|
|
|
After this change:
|
|
|
|
- gyroscope publications stopped;
|
|
- temperature continued to update;
|
|
- accelerometer publications unexpectedly disappeared;
|
|
- the application reported `IMU DATA INVALID`.
|
|
|
|
Because temperature was still read successfully, the IMU and I2C
|
|
communication were still functioning. This pointed to the interpretation
|
|
of `STATUS_REG`.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 5. STATUS_REG diagnostic
|
|
|
|
Temporary instrumentation was added after reading register `0x1E`:
|
|
|
|
``` cpp
|
|
uint8_t status;
|
|
if (!this->read_register(0x1E, &status, 1))
|
|
return;
|
|
|
|
ESP_LOGW(TAG, "STATUS=0x%02X accel_odr=0x%02X gyro_odr=0x%02X",
|
|
status,
|
|
static_cast<uint8_t>(this->accel_odr_),
|
|
static_cast<uint8_t>(this->gyro_odr_));
|
|
```
|
|
|
|
Diagnostic commits:
|
|
|
|
``` text
|
|
e2d4b947e lsm6ds: add temporary status diagnostics
|
|
53218bd4d lsm6ds: raise temporary status diagnostic to warning
|
|
```
|
|
|
|
With:
|
|
|
|
``` text
|
|
accel_odr = 0x04 (104 Hz)
|
|
gyro_odr = 0x00 (OFF)
|
|
```
|
|
|
|
the device repeatedly reported:
|
|
|
|
``` text
|
|
STATUS=0x01 accel_odr=0x04 gyro_odr=0x00
|
|
STATUS=0x05 accel_odr=0x04 gyro_odr=0x00
|
|
```
|
|
|
|
`0x02` was not observed.
|
|
|
|
`0x05` is `0x01 | 0x04` and appeared when temperature data was also
|
|
available. This demonstrated that `0x01` was the accelerometer-ready
|
|
flag, not the gyroscope-ready flag.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 6. Confirmed STATUS_REG bug
|
|
|
|
Correct mapping:
|
|
|
|
Bit Mask Meaning
|
|
----- -------- ---------------------------------------
|
|
0 `0x01` XLDA --- accelerometer data available
|
|
1 `0x02` GDA --- gyroscope data available
|
|
2 `0x04` TDA --- temperature data available
|
|
|
|
The external component had XLDA and GDA reversed.
|
|
|
|
Previous logic was effectively:
|
|
|
|
``` cpp
|
|
if (status & 0x01) { // GDA - incorrect
|
|
// read gyroscope
|
|
}
|
|
|
|
if (status & 0x02) { // XLDA - incorrect
|
|
// read accelerometer
|
|
}
|
|
```
|
|
|
|
Corrected implementation:
|
|
|
|
``` cpp
|
|
if (this->gyro_odr_ != LSM6DS_ODR_OFF && (status & 0x02)) { // GDA
|
|
// read gyroscope
|
|
}
|
|
|
|
if (this->accel_odr_ != LSM6DS_ODR_OFF && (status & 0x01)) { // XLDA
|
|
// read accelerometer
|
|
}
|
|
```
|
|
|
|
Temperature remains:
|
|
|
|
``` cpp
|
|
if (status & 0x04) { // TDA
|
|
// read temperature
|
|
}
|
|
```
|
|
|
|
The temporary diagnostic logging was removed in the final correction.
|
|
|
|
Commit:
|
|
|
|
``` text
|
|
7adb22de2 lsm6ds: fix swapped accel and gyro status bits
|
|
```
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 7. Validation
|
|
|
|
The firmware containing commit `7adb22de2` was deployed with:
|
|
|
|
``` text
|
|
Accelerometer ODR: 104 Hz
|
|
Gyroscope ODR: OFF
|
|
Polling interval: 10 ms
|
|
```
|
|
|
|
After correction:
|
|
|
|
- accelerometer X/Y/Z publishes normally;
|
|
- gyroscope values no longer publish;
|
|
- temperature continues to update;
|
|
- raw and calibrated pitch/roll publish normally;
|
|
- orientation calibration reports READY;
|
|
- RV Level diagnostic reports `OK`;
|
|
- activation from INACTIVE to ACTIVE works normally.
|
|
|
|
This confirms the XLDA/GDA mask error independently of the watchdog
|
|
investigation.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 8. Relevant commit history
|
|
|
|
``` text
|
|
7adb22de2 lsm6ds: fix swapped accel and gyro status bits
|
|
53218bd4d lsm6ds: raise temporary status diagnostic to warning
|
|
e2d4b947e lsm6ds: add temporary status diagnostics
|
|
3b86faa44 lsm6ds: skip reads for disabled sensors
|
|
41cb715d6 lsm6ds: expose OFF output data rate
|
|
```
|
|
|
|
Branch: `rv-lsm6ds`
|
|
|
|
Repository: `https://git.mr2web.net/mr2web/esphome-lsm6ds.git`
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 9. Interrupt-watchdog relationship
|
|
|
|
The STATUS_REG bug was discovered while investigating recurring ESP32-C6
|
|
resets reporting:
|
|
|
|
``` text
|
|
Reason: Interrupt wdt
|
|
Crashed core: 0
|
|
```
|
|
|
|
Earlier firmware polled the LSM6DS every 10 ms with both accelerometer
|
|
and gyroscope configured at high rate. The leveling application requires
|
|
accelerometer data but not gyroscope data.
|
|
|
|
The corrected firmware removes unnecessary gyroscope activity and fixes
|
|
incorrect status-bit handling.
|
|
|
|
### Confirmed
|
|
|
|
1. XLDA and GDA status masks were reversed.
|
|
2. `0x01` is XLDA and `0x02` is GDA.
|
|
3. The configuration schema did not expose the already-supported C++
|
|
`LSM6DS_ODR_OFF` value.
|
|
4. A sensor configured with ODR OFF should not be read by the polling
|
|
path.
|
|
5. With corrected masks, accelerometer at 104 Hz and gyroscope OFF
|
|
operate as expected.
|
|
|
|
### Not yet proven
|
|
|
|
It has **not** been established that the reversed status bits,
|
|
unnecessary gyroscope path, or their combined workload caused the
|
|
ESP32-C6 interrupt-watchdog resets.
|
|
|
|
The corrected firmware is being left unchanged for an endurance test.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## 10. Exact endurance-test firmware artifacts
|
|
|
|
Exact ELF preserved on the RockPi:
|
|
|
|
``` text
|
|
/srv/docker/esphome/firmware-archive/rv-level/2026-09-20_2148/firmware.elf
|
|
```
|
|
|
|
SHA-256:
|
|
|
|
``` text
|
|
dca98d924fb09c63646d59a1ed7a10e48790692ae67c51bf260bd0275b2d5171
|
|
```
|
|
|
|
Corresponding OTA image:
|
|
|
|
``` text
|
|
/srv/docker/esphome/firmware-archive/rv-level/2026-09-20_2148/firmware.ota.bin
|
|
```
|
|
|
|
SHA-256:
|
|
|
|
``` text
|
|
1e7dc78e4b40fe7b1f7f31294c70968b5cf6736840e4fd37926a64afd1477d67
|
|
```
|
|
|
|
These came from the ESPHome remote build generated at approximately
|
|
21:48 on 2026-09-20.
|
|
|
|
If another watchdog reset occurs, its `PC` and `BT` addresses should be
|
|
decoded against this exact `firmware.elf`, not a rebuilt ELF.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
# Upstream Bug Report
|
|
|
|
## LSM6DS STATUS_REG XLDA/GDA masks are reversed
|
|
|
|
The LSM6DS base component has the XLDA and GDA bits of `STATUS_REG`
|
|
(`0x1E`) reversed.
|
|
|
|
Affected logic treated:
|
|
|
|
``` cpp
|
|
status & 0x01 // GDA
|
|
status & 0x02 // XLDA
|
|
```
|
|
|
|
Correct mapping:
|
|
|
|
``` text
|
|
bit 0 / 0x01 = XLDA — accelerometer data available
|
|
bit 1 / 0x02 = GDA — gyroscope data available
|
|
bit 2 / 0x04 = TDA — temperature data available
|
|
```
|
|
|
|
## Reproduction
|
|
|
|
Test environment:
|
|
|
|
- LSM6DSOXTR
|
|
- ESP32-C6
|
|
- ESPHome 2026.8.2
|
|
- I2C
|
|
- accelerometer ODR: 104 Hz
|
|
- gyroscope ODR: OFF
|
|
- polling interval: 10 ms
|
|
|
|
Temporary logging with:
|
|
|
|
``` text
|
|
accel_odr=0x04
|
|
gyro_odr=0x00
|
|
```
|
|
|
|
repeatedly produced:
|
|
|
|
``` text
|
|
STATUS=0x01 accel_odr=0x04 gyro_odr=0x00
|
|
STATUS=0x05 accel_odr=0x04 gyro_odr=0x00
|
|
```
|
|
|
|
`0x02` was not observed.
|
|
|
|
This is consistent with bit 0 being XLDA and bit 2 being TDA.
|
|
|
|
## Fix
|
|
|
|
``` cpp
|
|
if (this->gyro_odr_ != LSM6DS_ODR_OFF && (status & 0x02)) { // GDA
|
|
...
|
|
}
|
|
|
|
if (this->accel_odr_ != LSM6DS_ODR_OFF && (status & 0x01)) { // XLDA
|
|
...
|
|
}
|
|
```
|
|
|
|
After correction with accelerometer at 104 Hz and gyroscope OFF:
|
|
|
|
- accelerometer values update normally;
|
|
- no gyroscope values are read/published;
|
|
- temperature continues to update normally.
|
|
|
|
Fix commit:
|
|
|
|
``` text
|
|
7adb22de2 lsm6ds: fix swapped accel and gyro status bits
|
|
```
|
|
|
|
Related work also exposes `OFF` as an ODR configuration option and
|
|
prevents reads for an output whose ODR is OFF. Those changes are
|
|
separable from the XLDA/GDA correction.
|
|
|
|
No claim is currently made that the status-bit bug caused the separately
|
|
observed ESP32-C6 interrupt-watchdog resets.
|