10 KiB
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:
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:
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:
LSM6DS_ODR_OFF = 0x00
but the ESPHome configuration schema did not expose OFF.
This was corrected so YAML could use:
gyro_odr: "OFF"
OFF is quoted because an unquoted YAML value may be interpreted as
boolean false.
Commit:
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:
if (this->gyro_odr_ != LSM6DS_ODR_OFF && ...) {
// gyro read
}
if (this->accel_odr_ != LSM6DS_ODR_OFF && ...) {
// accelerometer read
}
Commit:
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:
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:
e2d4b947e lsm6ds: add temporary status diagnostics
53218bd4d lsm6ds: raise temporary status diagnostic to warning
With:
accel_odr = 0x04 (104 Hz)
gyro_odr = 0x00 (OFF)
the device repeatedly reported:
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:
if (status & 0x01) { // GDA - incorrect
// read gyroscope
}
if (status & 0x02) { // XLDA - incorrect
// read accelerometer
}
Corrected implementation:
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:
if (status & 0x04) { // TDA
// read temperature
}
The temporary diagnostic logging was removed in the final correction.
Commit:
7adb22de2 lsm6ds: fix swapped accel and gyro status bits
7. Validation
The firmware containing commit 7adb22de2 was deployed with:
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
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:
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
- XLDA and GDA status masks were reversed.
0x01is XLDA and0x02is GDA.- The configuration schema did not expose the already-supported C++
LSM6DS_ODR_OFFvalue. - A sensor configured with ODR OFF should not be read by the polling path.
- 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:
/srv/docker/esphome/firmware-archive/rv-level/2026-09-20_2148/firmware.elf
SHA-256:
dca98d924fb09c63646d59a1ed7a10e48790692ae67c51bf260bd0275b2d5171
Corresponding OTA image:
/srv/docker/esphome/firmware-archive/rv-level/2026-09-20_2148/firmware.ota.bin
SHA-256:
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:
status & 0x01 // GDA
status & 0x02 // XLDA
Correct mapping:
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:
accel_odr=0x04
gyro_odr=0x00
repeatedly produced:
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
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:
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.