ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
lsm6ds.cpp
Go to the documentation of this file.
1#include "lsm6ds.h"
2#include "esphome/core/log.h"
3#include "esphome/core/hal.h"
5
6namespace esphome::lsm6ds {
7
8ESPHOME_LOG_TAG(TAG, "lsm6ds");
9
10// Indexed by the accel range field, which is not in ascending order (datasheet Table 3)
11PROGMEM_STRING_TABLE(AccelRangeStrings, "±2g", "±16g", "±4g", "±8g");
12
13static const LogString *gyro_range_to_str(LSM6DSGyroRange range) {
14 switch (range) {
16 return LOG_STR("±125dps");
18 return LOG_STR("±250dps");
20 return LOG_STR("±500dps");
22 return LOG_STR("±1000dps");
24 return LOG_STR("±2000dps");
25 default:
26 return LOG_STR("unknown");
27 }
28}
29
30const LogString *chip_name_from_who_am_i(uint8_t who_am_i) {
31 switch (who_am_i) {
32 case LSM6DS_WHO_AM_I_LSM6DS3:
33 return LOG_STR("LSM6DS3");
34 case LSM6DS_WHO_AM_I_LSM6DS3TR_C:
35 return LOG_STR("LSM6DS3TR-C");
36 case LSM6DS_WHO_AM_I_LSM6DSO:
37 return LOG_STR("LSM6DSO");
38 default:
39 return nullptr;
40 }
41}
42
43uint16_t temperature_sensitivity_from_who_am_i(uint8_t who_am_i) {
44 return who_am_i == LSM6DS_WHO_AM_I_LSM6DS3 ? 16 : 256;
45}
46
48 MotionComponent::setup();
49 uint8_t who_am_i = 0;
50 if (this->read_register(LSM6DS_REG_WHO_AM_I, &who_am_i, 1) != i2c::ERROR_OK) {
51 ESP_LOGE(TAG, "Failed to read WHO_AM_I — check wiring and I2C address");
52 this->mark_failed();
53 return;
54 }
55 const LogString *chip_name = chip_name_from_who_am_i(who_am_i);
56 if (chip_name == nullptr) {
57 ESP_LOGE(TAG, "Unknown WHO_AM_I: 0x%02X", who_am_i);
58 this->mark_failed(LOG_STR("Unknown WHO_AM_I value"));
59 return;
60 }
61 ESP_LOGD(TAG, "Found %s (WHO_AM_I = 0x%02X)", LOG_STR_ARG(chip_name), who_am_i);
62 this->chip_name_ = chip_name;
64
65 // 2. Software reset — clears all registers to defaults
66 if (this->write_register(LSM6DS_REG_CTRL3_C, &CTRL3_C_SW_RESET, 1) != i2c::ERROR_OK) {
67 this->mark_failed(LOG_STR("Software reset failed"));
68 return;
69 }
70 // Datasheet: reset bit self-clears after boot (typ. 50 µs);
71 delay(2);
72
73 // 3. Enable auto-increment and block data update (BDU).
74 // BDU prevents reading a high-byte from one sample and a low-byte from the next.
75 // IF_INC is set by default after reset but we set it explicitly for clarity.
76 uint8_t ctrl3 = CTRL3_C_IF_INC | CTRL3_C_BDU;
77 if (this->write_register(LSM6DS_REG_CTRL3_C, &ctrl3, 1) != i2c::ERROR_OK) {
78 this->mark_failed(LOG_STR("Config failed"));
79 return;
80 }
81
82 // 4. Configure accelerometer: ODR in bits[7:4], FS in bits[3:2]
83 // Anti-aliasing filter bandwidth is left at power-on default (bits[1:0] = 00 = ODR/2).
84 uint8_t ctrl1_xl = (uint8_t) (this->accel_odr_ << 4) | (uint8_t) (this->accel_range_ << 2);
85 if (this->write_register(LSM6DS_REG_CTRL1_XL, &ctrl1_xl, 1) != i2c::ERROR_OK) {
86 this->mark_failed(LOG_STR("Failed to configure accelerometer"));
87 return;
88 }
89
90 // 5. Configure gyroscope: ODR in bits[7:4], FS_G + FS_125 in bits[3:0]
91 // For ±125 dps: FS_G[2:1]=00 and FS_125(bit1)=1, so gyro_range_ encodes the full nibble.
92 uint8_t ctrl2_g = (uint8_t) (this->gyro_odr_ << 4) | (uint8_t) (this->gyro_range_);
93 if (this->write_register(LSM6DS_REG_CTRL2_G, &ctrl2_g, 1) != i2c::ERROR_OK) {
94 this->mark_failed(LOG_STR("Failed to configure gyroscope"));
95 return;
96 }
97
98 // 6. Ensure accelerometer is in high-performance mode (CTRL6_C bit 4 = XL_HM_MODE = 0)
99 // and gyroscope is in high-performance mode (CTRL7_G bit 7 = G_HM_MODE = 0).
100 // Both default to 0 (high-performance) after reset, but write explicitly.
101 uint8_t zero = 0x00;
102 if (this->write_register(LSM6DS_REG_CTRL6_C, &zero, 1) != i2c::ERROR_OK) {
103 this->mark_failed();
104 return;
105 }
106 if (this->write_register(LSM6DS_REG_CTRL7_G, &zero, 1) != i2c::ERROR_OK) {
107 this->mark_failed();
108 return;
109 }
110}
111
113 ESP_LOGCONFIG(TAG,
114 "LSM6DS IMU:\n"
115 " Chip type: %s\n",
116 LOG_STR_ARG(this->chip_name_ != nullptr ? this->chip_name_ : LOG_STR("Unknown")));
117 LOG_I2C_DEVICE(this);
118 LOG_UPDATE_INTERVAL(this);
119
120 auto accel_odr = this->accel_odr_ == 0 ? 0 : 13 * (1 << (this->accel_odr_ - 1));
121 auto gyro_odr = this->gyro_odr_ == 0 ? 0 : 13 * (1 << (this->gyro_odr_ - 1));
122 ESP_LOGCONFIG(TAG,
123 " Accel range : %s\n"
124 " Accel data rate : %dHz\n"
125 " Gyro range : %s\n"
126 " Gyro data rate : %dHz",
127 LOG_STR_ARG(AccelRangeStrings::get_log_str(this->accel_range_, AccelRangeStrings::LAST_INDEX)),
128 accel_odr, LOG_STR_ARG(gyro_range_to_str(this->gyro_range_)), gyro_odr);
129}
130
131// update_data()
132// Called by MotionComponent::update() on each polling interval.
133// Reads gyro XYZ and accel XYZ in a single 12-byte burst (registers 0x22–0x2D).
134// Values are in g (accel) and °/s (gyro) — MotionComponent handles axis mapping
135// and sensor publishing.
136
138 if (this->is_failed())
139 return false;
140
141 // Single burst: gyro X/Y/Z (0x22–0x27) then accel X/Y/Z (0x28–0x2D)
142 uint8_t raw[LSM6DS_BURST_LEN];
143 if (!this->read_bytes(LSM6DS_REG_OUTX_L_G, raw, LSM6DS_BURST_LEN)) {
144 this->status_set_error(LOG_STR("Failed to read IMU data"));
145 return false;
146 }
147 this->status_clear_error();
148
149 // Gyroscope
150 // Sensitivity (mdps/LSB) from datasheet Table 3.
151 // Multiply by 1e-3 to convert mdps → dps (°/s).
152 static constexpr float GYRO_SCALE[] = {
153 8.75e-3f, // 0x00 — ±250 dps
154 8.75e-3f, // 0x01 — unused (maps to 250 as fallback)
155 4.375e-3f, // 0x02 — ±125 dps (FS_125 bit set)
156 8.75e-3f, // 0x03 — unused
157 17.50e-3f, // 0x04 — ±500 dps
158 17.50e-3f, // 0x05 — unused
159 8.75e-3f, // 0x06 — unused
160 8.75e-3f, // 0x07 — unused
161 35.0e-3f, // 0x08 — ±1000 dps
162 35.0e-3f, // 0x09 — unused
163 17.50e-3f, // 0x0A — unused
164 17.50e-3f, // 0x0B — unused
165 70.0e-3f, // 0x0C — ±2000 dps
166 };
167 float gyro_scale = GYRO_SCALE[this->gyro_range_];
168
169 data.angular_rate[motion::X_AXIS] = (int16_t) ((raw[1] << 8) | raw[0]) * gyro_scale;
170 data.angular_rate[motion::Y_AXIS] = (int16_t) ((raw[3] << 8) | raw[2]) * gyro_scale;
171 data.angular_rate[motion::Z_AXIS] = (int16_t) ((raw[5] << 8) | raw[4]) * gyro_scale;
172
173 // Accelerometer
174 // Sensitivity (mg/LSB) from datasheet Table 3.
175 // Multiply by 1e-3 to convert mg → g.
176 // Note: FS_XL register values are non-monotonic (0=2g, 1=16g, 2=4g, 3=8g).
177 static constexpr float ACCEL_SCALE[] = {
178 0.061e-3f, // 0x00 — ±2g
179 0.488e-3f, // 0x01 — ±16g
180 0.122e-3f, // 0x02 — ±4g
181 0.244e-3f, // 0x03 — ±8g
182 };
183 float accel_scale = ACCEL_SCALE[this->accel_range_];
184
185 data.acceleration[motion::X_AXIS] =
186 (int16_t) ((raw[LSM6DS_ACCEL_OFFSET + 1] << 8) | raw[LSM6DS_ACCEL_OFFSET + 0]) * accel_scale;
187 data.acceleration[motion::Y_AXIS] =
188 (int16_t) ((raw[LSM6DS_ACCEL_OFFSET + 3] << 8) | raw[LSM6DS_ACCEL_OFFSET + 2]) * accel_scale;
189 data.acceleration[motion::Z_AXIS] =
190 (int16_t) ((raw[LSM6DS_ACCEL_OFFSET + 5] << 8) | raw[LSM6DS_ACCEL_OFFSET + 4]) * accel_scale;
191
192 // Temperature (lazy — only read if a listener is registered)
193 // Kept as a separate 2-byte read to avoid extending the burst to 14 bytes when
194 // temperature is not needed.
195 // Formula: T(°C) = raw / sensitivity + 25.0, sensitivity in LSB/°C per chip (0 LSB at 25 °C)
196 if (!this->temperature_callback_.empty()) {
197 uint8_t raw_t[2];
198 if (this->read_bytes(LSM6DS_REG_OUT_TEMP_L, raw_t, 2)) {
199 int16_t temp_raw = (int16_t) ((raw_t[1] << 8) | raw_t[0]);
200 float temperature = temp_raw / static_cast<float>(this->temperature_sensitivity_) + 25.0f;
201 this->temperature_callback_.call(temperature);
202 }
203 }
204
205 return true;
206}
207
208} // namespace esphome::lsm6ds
uint8_t raw[35]
Definition bl0939.h:0
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void status_clear_error()
Definition component.h:297
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) const
writes an array of bytes to a specific register in the I²C device
Definition i2c.cpp:34
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len)
reads an array of bytes from a specific register in the I²C device
Definition i2c.cpp:25
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:217
LazyCallbackManager< void(float)> temperature_callback_
Definition lsm6ds.h:119
bool update_data(motion::MotionData &data) override
Definition lsm6ds.cpp:137
LSM6DSGyroRange gyro_range_
Definition lsm6ds.h:115
LSM6DSAccelRange accel_range_
Definition lsm6ds.h:113
const LogString * chip_name_
Definition lsm6ds.h:110
Range range
Definition msa3xx.h:0
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
uint16_t temperature_sensitivity_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.
Definition lsm6ds.cpp:43
ESPHOME_LOG_TAG(TAG, "lsm6ds")
const LogString * chip_name_from_who_am_i(uint8_t who_am_i)
Chip name for a WHO_AM_I value, or nullptr when the chip is not supported.
Definition lsm6ds.cpp:30
@ LSM6DS_GYRO_RANGE_250
Definition lsm6ds.h:67
@ LSM6DS_GYRO_RANGE_125
Definition lsm6ds.h:66
@ LSM6DS_GYRO_RANGE_2000
Definition lsm6ds.h:70
@ LSM6DS_GYRO_RANGE_1000
Definition lsm6ds.h:69
@ LSM6DS_GYRO_RANGE_500
Definition lsm6ds.h:68
PROGMEM_STRING_TABLE(AccelRangeStrings, "±2g", "±16g", "±4g", "±8g")
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint16_t temperature
Definition sun_gtil2.cpp:12