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