docs: document LSM6DS status register bug investigation
This commit is contained in:
1 parent
7adb22de2c
commit
bbdb141e19
1 file changed
+436
@@ -0,0 +1,436 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user