[lsm6ds] Add the LSM6DSO and match all chips to their datasheets (#20733)

This commit is contained in:
J. Nick Koston
2026-10-14 19:24:00 -10:00
committed by GitHub
parent 41e82b0295
commit ea6d5499b3
4 changed files with 76 additions and 15 deletions
+13 -6
View File
@@ -27,17 +27,23 @@ static const LogString *gyro_range_to_str(LSM6DSGyroRange range) {
}
}
static const LogString *chip_name_from_who_am_i(uint8_t who_am_i) {
const LogString *chip_name_from_who_am_i(uint8_t who_am_i) {
switch (who_am_i) {
case 0x69:
return LOG_STR("LSMDSO");
case 0x6A:
case LSM6DS_WHO_AM_I_LSM6DS3:
return LOG_STR("LSM6DS3");
case LSM6DS_WHO_AM_I_LSM6DS3TR_C:
return LOG_STR("LSM6DS3TR-C");
case LSM6DS_WHO_AM_I_LSM6DSO:
return LOG_STR("LSM6DSO");
default:
return nullptr;
}
}
uint16_t temperature_sensitivity_from_who_am_i(uint8_t who_am_i) {
return who_am_i == LSM6DS_WHO_AM_I_LSM6DS3 ? 16 : 256;
}
void LSM6DSComponent::setup() {
MotionComponent::setup();
uint8_t who_am_i = 0;
@@ -54,6 +60,7 @@ void LSM6DSComponent::setup() {
}
ESP_LOGD(TAG, "Found %s (WHO_AM_I = 0x%02X)", LOG_STR_ARG(chip_name), who_am_i);
this->chip_name_ = chip_name;
this->temperature_sensitivity_ = temperature_sensitivity_from_who_am_i(who_am_i);
// 2. Software reset — clears all registers to defaults
if (this->write_register(LSM6DS_REG_CTRL3_C, &CTRL3_C_SW_RESET, 1) != i2c::ERROR_OK) {
@@ -185,12 +192,12 @@ bool LSM6DSComponent::update_data(motion::MotionData &data) {
// Temperature (lazy — only read if a listener is registered)
// Kept as a separate 2-byte read to avoid extending the burst to 14 bytes when
// temperature is not needed.
// Formula: T(°C) = (raw / 256.0) + 25.0 (datasheet Table 90, OUT_TEMP register)
// Formula: T(°C) = raw / sensitivity + 25.0, sensitivity in LSB/°C per chip (0 LSB at 25 °C)
if (!this->temperature_callback_.empty()) {
uint8_t raw_t[2];
if (this->read_bytes(LSM6DS_REG_OUT_TEMP_L, raw_t, 2)) {
int16_t temp_raw = (int16_t) ((raw_t[1] << 8) | raw_t[0]);
float temperature = (temp_raw / 256.0f) + 25.0f;
float temperature = temp_raw / static_cast<float>(this->temperature_sensitivity_) + 25.0f;
this->temperature_callback_.call(temperature);
}
}
+19 -9
View File
@@ -9,15 +9,19 @@ namespace esphome::lsm6ds {
// ── Register map (datasheet DocID030071 Rev 3, Table 19) ────────────────────
static const uint8_t LSM6DS_REG_WHO_AM_I = 0x0F;
static const uint8_t LSM6DS_REG_CTRL1_XL = 0x10; // Accel ODR + FS
static const uint8_t LSM6DS_REG_CTRL2_G = 0x11; // Gyro ODR + FS
static const uint8_t LSM6DS_REG_CTRL3_C = 0x12; // SW_RESET, BDU, IF_INC
static const uint8_t LSM6DS_REG_CTRL6_C = 0x15; // Accel HP disable, Gyro LPF1
static const uint8_t LSM6DS_REG_CTRL7_G = 0x16; // Gyro HP disable
static const uint8_t LSM6DS_REG_STATUS = 0x1E; // XLDA, GDA, TDA
static const uint8_t LSM6DS_REG_OUT_TEMP_L = 0x20; // Temperature LSB
static const uint8_t LSM6DS_REG_OUTX_L_G = 0x22; // Gyro X LSB (burst start)
static const uint8_t LSM6DS_REG_OUTX_L_XL = 0x28; // Accel X LSB
// WHO_AM_I values; the LSM6DS3 (DocID026899) and LSM6DSO (DS12140) use the same registers as the TR-C here
static constexpr uint8_t LSM6DS_WHO_AM_I_LSM6DS3 = 0x69;
static constexpr uint8_t LSM6DS_WHO_AM_I_LSM6DS3TR_C = 0x6A;
static constexpr uint8_t LSM6DS_WHO_AM_I_LSM6DSO = 0x6C; // also the LSM6DSO32, whose accel ranges differ (unsupported)
static const uint8_t LSM6DS_REG_CTRL1_XL = 0x10; // Accel ODR + FS
static const uint8_t LSM6DS_REG_CTRL2_G = 0x11; // Gyro ODR + FS
static const uint8_t LSM6DS_REG_CTRL3_C = 0x12; // SW_RESET, BDU, IF_INC
static const uint8_t LSM6DS_REG_CTRL6_C = 0x15; // Accel HP disable, Gyro LPF1
static const uint8_t LSM6DS_REG_CTRL7_G = 0x16; // Gyro HP disable
static const uint8_t LSM6DS_REG_STATUS = 0x1E; // XLDA, GDA, TDA
static const uint8_t LSM6DS_REG_OUT_TEMP_L = 0x20; // Temperature LSB
static const uint8_t LSM6DS_REG_OUTX_L_G = 0x22; // Gyro X LSB (burst start)
static const uint8_t LSM6DS_REG_OUTX_L_XL = 0x28; // Accel X LSB
// Burst read from 0x22 to 0x2D inclusive: gyro XYZ (6 bytes) + accel XYZ (6 bytes)
static const uint8_t LSM6DS_BURST_LEN = 12;
@@ -83,6 +87,11 @@ enum LSM6DSGyroODR : uint8_t {
};
// ── Main component class ─────────────────────────────────────────────────────
/// Chip name for a WHO_AM_I value, or nullptr when the chip is not supported.
const LogString *chip_name_from_who_am_i(uint8_t who_am_i);
/// Temperature sensitivity in LSB/°C for a supported WHO_AM_I value, from the chip's datasheet.
uint16_t temperature_sensitivity_from_who_am_i(uint8_t who_am_i);
class LSM6DSComponent final : public motion::MotionComponent, public i2c::I2CDevice {
public:
void setup() override;
@@ -105,6 +114,7 @@ class LSM6DSComponent final : public motion::MotionComponent, public i2c::I2CDev
LSM6DSAccelODR accel_odr_{LSM6DS_ACCEL_ODR_104};
LSM6DSGyroRange gyro_range_{LSM6DS_GYRO_RANGE_2000};
LSM6DSGyroODR gyro_odr_{LSM6DS_GYRO_ODR_208};
uint16_t temperature_sensitivity_{256}; // LSB/°C: 16 on the LSM6DS3, 256 on the others
LazyCallbackManager<void(float)> temperature_callback_{};
};
+6
View File
@@ -0,0 +1,6 @@
from tests.testing_helpers import ComponentManifestOverride
def override_manifest(manifest: ComponentManifestOverride) -> None:
# lsm6ds declares i2c on its motion platform, which the harness does not walk
manifest.dependencies = manifest.dependencies + ["i2c", "motion"]
+38
View File
@@ -0,0 +1,38 @@
#include <gtest/gtest.h>
#include <cstring>
#include "esphome/components/lsm6ds/lsm6ds.h"
namespace esphome::lsm6ds::testing {
// WHO_AM_I values and temperature sensitivities from ST's datasheets:
// LSM6DS3 DocID026899, LSM6DS3TR-C DocID030071, LSM6DSO DS12140
static const char *name_of(uint8_t who_am_i) {
const LogString *name = chip_name_from_who_am_i(who_am_i);
return name == nullptr ? nullptr : LOG_STR_ARG(name);
}
TEST(LSM6DSChipIds, Lsm6ds3) {
EXPECT_STREQ(name_of(0x69), "LSM6DS3");
EXPECT_EQ(temperature_sensitivity_from_who_am_i(0x69), 16);
}
TEST(LSM6DSChipIds, Lsm6ds3trC) {
EXPECT_STREQ(name_of(0x6A), "LSM6DS3TR-C");
EXPECT_EQ(temperature_sensitivity_from_who_am_i(0x6A), 256);
}
TEST(LSM6DSChipIds, Lsm6dso) {
EXPECT_STREQ(name_of(0x6C), "LSM6DSO");
EXPECT_EQ(temperature_sensitivity_from_who_am_i(0x6C), 256);
}
TEST(LSM6DSChipIds, UnknownIsRejected) {
EXPECT_EQ(name_of(0x00), nullptr);
EXPECT_EQ(name_of(0x6B), nullptr);
EXPECT_EQ(name_of(0xFF), nullptr);
}
} // namespace esphome::lsm6ds::testing