Files
esphome-lsm6ds/docs/lsm6ds-status-register-bug.md
T

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.