ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
lis2dw12.cpp
Go to the documentation of this file.
1#include "lis2dw12.h"
3#include "esphome/core/hal.h"
4#include "esphome/core/log.h"
5
7
8ESPHOME_LOG_TAG(TAG, "lis2dw12");
9
10static constexpr uint8_t LIS2DW12_WHO_AM_I_VALUE = 0x44;
11static constexpr float GRAVITY_EARTH = 9.80665f;
12static constexpr uint32_t EVENT_COOLDOWN_MS = 500;
13static constexpr uint32_t POLL_INTERVAL_MS = 50;
14static constexpr uint8_t SIXD_POSITION_MASK = 0x3F; // XL..ZH, leaving out D6D_IA
15
16static const LogString *power_mode_to_string(PowerMode mode) {
17 switch (mode) {
19 return LOG_STR("High Performance");
21 return LOG_STR("Low Power 1 (12-bit)");
23 return LOG_STR("Low Power 2");
25 return LOG_STR("Low Power 3");
27 return LOG_STR("Low Power 4");
28 }
29 return LOG_STR("Unknown");
30}
31
32static const LogString *range_to_string(Range range) {
33 switch (range) {
34 case Range::RANGE_2G:
35 return LOG_STR("±2g");
36 case Range::RANGE_4G:
37 return LOG_STR("±4g");
38 case Range::RANGE_8G:
39 return LOG_STR("±8g");
41 return LOG_STR("±16g");
42 }
43 return LOG_STR("Unknown");
44}
45
46static const LogString *data_rate_to_string(DataRate rate) {
47 switch (rate) {
49 return LOG_STR("1.6 Hz");
51 return LOG_STR("12.5 Hz");
53 return LOG_STR("25 Hz");
55 return LOG_STR("50 Hz");
57 return LOG_STR("100 Hz");
59 return LOG_STR("200 Hz");
61 return LOG_STR("400 Hz");
63 return LOG_STR("800 Hz");
65 return LOG_STR("1600 Hz");
66 }
67 return LOG_STR("Unknown");
68}
69
70static const LogString *filter_bandwidth_to_string(FilterBandwidth bw) {
71 switch (bw) {
73 return LOG_STR("ODR/2");
75 return LOG_STR("ODR/4");
77 return LOG_STR("ODR/10");
79 return LOG_STR("ODR/20");
80 }
81 return LOG_STR("Unknown");
82}
83
85 uint8_t who_am_i{0};
86 if (!this->read_byte(static_cast<uint8_t>(RegisterMap::WHO_AM_I), &who_am_i)) {
87 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
88 return;
89 }
90 if (who_am_i != LIS2DW12_WHO_AM_I_VALUE) {
91 ESP_LOGE(TAG, "WHO_AM_I check failed. Got 0x%02X, expected 0x%02X", who_am_i, LIS2DW12_WHO_AM_I_VALUE);
92 this->mark_failed(LOG_STR("Wrong chip ID"));
93 return;
94 }
95
96 // Soft reset
97 if (!this->write_reg_(RegisterMap::CTRL2, 0x40)) {
98 this->mark_failed(LOG_STR("Soft reset failed"));
99 return;
100 }
101 delay(1); // NOLINT - wait for reset to complete
102
103 uint8_t ctrl2{0};
104 if (!this->read_byte(static_cast<uint8_t>(RegisterMap::CTRL2), &ctrl2) || (ctrl2 & 0x40)) {
105 this->mark_failed(LOG_STR("Soft reset did not complete"));
106 return;
107 }
108
109 if (!this->configure_registers_()) {
110 this->mark_failed(LOG_STR("Register configuration failed"));
111 return;
112 }
113
114 // 0.244 mg/LSB at ±2g (14-bit), doubling per range step; 12-bit LP1 is 4x coarser
115 const bool lp1 = this->power_mode_ == PowerMode::LOW_POWER_1;
116 const uint8_t range_idx = static_cast<uint8_t>(this->range_);
117 const float mg = 0.244f * (1u << range_idx) * (lp1 ? 4 : 1);
118 this->scale_ = mg * 0.001f * GRAVITY_EARTH;
119 this->shift_ = lp1 ? 4 : 2;
120
121 bool events_used = !this->tap_callback_.empty() || !this->double_tap_callback_.empty() ||
122 !this->freefall_callback_.empty() || !this->active_callback_.empty() ||
123 !this->orientation_callback_.empty();
124#ifdef USE_BINARY_SENSOR
125 for (auto *sensor : this->event_binary_sensors_) {
126 if (sensor != nullptr) {
127 sensor->publish_initial_state(false);
128 events_used = true;
129 }
130 }
131#endif
132#ifdef USE_TEXT_SENSOR
133 RegSixdSrc sixd;
134 if (this->orientation_text_sensor_ != nullptr &&
135 this->read_byte(static_cast<uint8_t>(RegisterMap::SIXD_SRC), &sixd.raw)) {
136 this->publish_orientation_(sixd);
137 events_used = true;
138 }
139#endif
140 if (!events_used) {
141 this->disable_loop(); // nothing consumes the events, so skip polling the chip
142 }
143}
144
146 // CTRL1: ODR[7:4], MODE[3:2] (00=low power, 01=high performance), LP_MODE[1:0] (LP1..LP4)
147 const uint8_t odr_bits = static_cast<uint8_t>(this->data_rate_) << 4;
148 uint8_t ctrl1;
149 if (this->power_mode_ == PowerMode::HIGH_PERF) {
150 ctrl1 = odr_bits | 0x04;
151 } else {
152 ctrl1 = odr_bits | ((static_cast<uint8_t>(this->power_mode_) - 1) & 0x03);
153 }
154
155 // CTRL6: BW_FILT[7:6], FS[5:4], LOW_NOISE[2]
156 uint8_t ctrl6 = (static_cast<uint8_t>(this->filter_bandwidth_) << 6) | (static_cast<uint8_t>(this->range_) << 4);
157 if (this->low_noise_) {
158 ctrl6 |= 0x04;
159 }
160
161 return this->write_reg_(RegisterMap::CTRL2, 0x0C) && // BDU, IF_ADD_INC
162 this->write_reg_(RegisterMap::CTRL1, ctrl1) && // ODR and power mode
163 this->write_reg_(RegisterMap::CTRL6, ctrl6) && // bandwidth, range, low noise
164 this->write_reg_(RegisterMap::TAP_THS_X, 0x29) && // 6D_THS=70 degrees, TAP_THSX=9
165 this->write_reg_(RegisterMap::TAP_THS_Y, 0x09) && // TAP_THSY=9
166 this->write_reg_(RegisterMap::TAP_THS_Z, 0xE9) && // TAP_THSZ=9, tap on X/Y/Z
167 this->write_reg_(RegisterMap::INT_DUR, 0x76) && // LATENCY=7, QUIET=1, SHOCK=2
168 this->write_reg_(RegisterMap::WAKE_UP_THS, 0x82) && // double tap enabled, WU_THS=2
169 this->write_reg_(RegisterMap::WAKE_UP_DUR, 0x00) && // no wake/sleep durations
170 this->write_reg_(RegisterMap::FREE_FALL, 0x33) && // FF_DUR=6, FF_THS=312 mg
171 this->write_reg_(RegisterMap::CTRL4_INT1, 0xF8) && // 6D, single tap, wake-up, free-fall, double tap to INT1
172 this->write_reg_(RegisterMap::CTRL3, 0x10) && // LIR: latched interrupts
173 this->write_reg_(RegisterMap::CTRL7, 0x20); // INTERRUPTS_ENABLE
174}
175
177 ESP_LOGCONFIG(TAG, "LIS2DW12:");
178 ESP_LOGCONFIG(TAG,
179 " Power Mode: %s\n"
180 " Data Rate: %s\n"
181 " Range: %s\n"
182 " Filter Bandwidth: %s\n"
183 " Low Noise: %s\n"
184 " Offsets: {%.3f m/s², %.3f m/s², %.3f m/s²}\n"
185 " Transform: {mirror_x=%s, mirror_y=%s, mirror_z=%s, swap_xy=%s}",
186 LOG_STR_ARG(power_mode_to_string(this->power_mode_)),
187 LOG_STR_ARG(data_rate_to_string(this->data_rate_)), LOG_STR_ARG(range_to_string(this->range_)),
188 LOG_STR_ARG(filter_bandwidth_to_string(this->filter_bandwidth_)), YESNO(this->low_noise_),
189 this->offset_x_, this->offset_y_, this->offset_z_, YESNO(this->mirror_x_), YESNO(this->mirror_y_),
190 YESNO(this->mirror_z_), YESNO(this->swap_xy_));
191 LOG_UPDATE_INTERVAL(this);
192
193#ifdef USE_BINARY_SENSOR
194 LOG_BINARY_SENSOR(" ", "Tap", this->event_binary_sensors_[EVENT_TAP]);
195 LOG_BINARY_SENSOR(" ", "Double Tap", this->event_binary_sensors_[EVENT_DOUBLE_TAP]);
196 LOG_BINARY_SENSOR(" ", "Freefall", this->event_binary_sensors_[EVENT_FREEFALL]);
197 LOG_BINARY_SENSOR(" ", "Active", this->event_binary_sensors_[EVENT_ACTIVE]);
198#endif
199
200#ifdef USE_SENSOR
201 LOG_SENSOR(" ", "Acceleration X", this->acceleration_x_sensor_);
202 LOG_SENSOR(" ", "Acceleration Y", this->acceleration_y_sensor_);
203 LOG_SENSOR(" ", "Acceleration Z", this->acceleration_z_sensor_);
204#endif
205
206#ifdef USE_TEXT_SENSOR
207 LOG_TEXT_SENSOR(" ", "Orientation", this->orientation_text_sensor_);
208#endif
209}
210
211bool LIS2DW12Component::read_acceleration_data_(float &x, float &y, float &z) {
212 uint8_t accel_data[6];
213 if (!this->read_bytes(static_cast<uint8_t>(RegisterMap::OUT_X_L), accel_data, 6)) {
214 return false;
215 }
216
217 int16_t raw_x = static_cast<int16_t>(encode_uint16(accel_data[1], accel_data[0])) >> this->shift_;
218 int16_t raw_y = static_cast<int16_t>(encode_uint16(accel_data[3], accel_data[2])) >> this->shift_;
219 int16_t raw_z = static_cast<int16_t>(encode_uint16(accel_data[5], accel_data[4])) >> this->shift_;
220
221 if (this->swap_xy_) {
222 std::swap(raw_x, raw_y);
223 }
224 x = (this->mirror_x_ ? -raw_x : raw_x) * this->scale_ + this->offset_x_;
225 y = (this->mirror_y_ ? -raw_y : raw_y) * this->scale_ + this->offset_y_;
226 z = (this->mirror_z_ ? -raw_z : raw_z) * this->scale_ + this->offset_z_;
227 return true;
228}
229
230#ifdef USE_TEXT_SENSOR
231static const char *orientation_to_string(RegSixdSrc sixd) {
232 if (sixd.xh)
233 return "X Up";
234 if (sixd.xl)
235 return "X Down";
236 if (sixd.yh)
237 return "Y Up";
238 if (sixd.yl)
239 return "Y Down";
240 if (sixd.zh)
241 return "Z Up";
242 if (sixd.zl)
243 return "Z Down";
244 return "Unknown";
245}
246
248 const uint8_t position = sixd.raw & SIXD_POSITION_MASK;
249 if (this->orientation_text_sensor_ == nullptr || position == this->sixd_last_) {
250 return;
251 }
252 this->sixd_last_ = position;
253 this->orientation_text_sensor_->publish_state(orientation_to_string(sixd));
254}
255#endif
256
257void LIS2DW12Component::handle_event_(Event event, bool active, uint32_t now, LazyCallbackManager<void()> &callback) {
258 const uint8_t bit = 1 << event;
259 bool state;
260 if (active) {
261 if (now - this->last_event_ms_[event] <= EVENT_COOLDOWN_MS) {
262 return;
263 }
264 this->last_event_ms_[event] = now;
265 callback.call();
266 if (this->events_on_ & bit) {
267 return;
268 }
269 this->events_on_ |= bit;
270 state = true;
271 } else {
272 if (!(this->events_on_ & bit) || now - this->last_event_ms_[event] <= EVENT_COOLDOWN_MS) {
273 return;
274 }
275 this->events_on_ &= ~bit;
276 state = false;
277 }
278#ifdef USE_BINARY_SENSOR
279 if (this->event_binary_sensors_[event] != nullptr) {
281 }
282#endif
283}
284
287 if (now - this->last_poll_ms_ < POLL_INTERVAL_MS) {
288 return;
289 }
290 this->last_poll_ms_ = now;
291
292 // SIXD_SRC and ALL_INT_SRC are adjacent, so one read captures both before any latch clears
293 uint8_t regs[2];
294 if (!this->read_bytes(static_cast<uint8_t>(RegisterMap::SIXD_SRC), regs, sizeof(regs))) {
295 this->status_set_warning();
296 return;
297 }
298 this->status_clear_warning();
299 RegSixdSrc sixd;
300 sixd.raw = regs[0];
302 src.raw = regs[1];
303
304 this->handle_event_(EVENT_TAP, src.single_tap, now, this->tap_callback_);
305 this->handle_event_(EVENT_DOUBLE_TAP, src.double_tap, now, this->double_tap_callback_);
306 this->handle_event_(EVENT_FREEFALL, src.ff_ia, now, this->freefall_callback_);
307 this->handle_event_(EVENT_ACTIVE, src.wu_ia, now, this->active_callback_);
308
309 if (sixd.d6d_ia) {
310 this->orientation_callback_.call();
311#ifdef USE_TEXT_SENSOR
312 this->publish_orientation_(sixd);
313#endif
314 }
315}
316
318 float x, y, z;
319 if (!this->read_acceleration_data_(x, y, z)) {
320 this->status_set_warning();
321 return;
322 }
323
324 ESP_LOGV(TAG, "Acceleration: {x = %+1.3f m/s², y = %+1.3f m/s², z = %+1.3f m/s²}", x, y, z);
325
326#ifdef USE_SENSOR
327 if (this->acceleration_x_sensor_ != nullptr)
328 this->acceleration_x_sensor_->publish_state(x);
329 if (this->acceleration_y_sensor_ != nullptr)
330 this->acceleration_y_sensor_->publish_state(y);
331 if (this->acceleration_z_sensor_ != nullptr)
332 this->acceleration_z_sensor_->publish_state(z);
333#endif
334
335 this->status_clear_warning();
336}
337
338void LIS2DW12Component::set_offset(float offset_x, float offset_y, float offset_z) {
339 this->offset_x_ = offset_x;
340 this->offset_y_ = offset_y;
341 this->offset_z_ = offset_z;
342}
343
344void LIS2DW12Component::set_transform(bool mirror_x, bool mirror_y, bool mirror_z, bool swap_xy) {
345 this->mirror_x_ = mirror_x;
346 this->mirror_y_ = mirror_y;
347 this->mirror_z_ = mirror_z;
348 this->swap_xy_ = swap_xy;
349}
350
351} // namespace esphome::lis2dw12
BedjetMode mode
BedJet operating mode.
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
void disable_loop()
Disable this component's loop.
void status_clear_warning()
Definition component.h:291
void publish_state(bool new_state)
Publish a new state to the front-end.
bool read_byte(uint8_t a_register, uint8_t *data)
Definition lis2dw12.h:126
LazyCallbackManager< void()> orientation_callback_
Definition lis2dw12.h:183
uint32_t last_event_ms_[EVENT_COUNT]
Definition lis2dw12.h:174
void publish_orientation_(RegSixdSrc sixd)
Definition lis2dw12.cpp:247
LazyCallbackManager< void()> double_tap_callback_
Definition lis2dw12.h:180
binary_sensor::BinarySensor * event_binary_sensors_[EVENT_COUNT]
Definition lis2dw12.h:177
bool write_reg_(RegisterMap reg, uint8_t value)
Definition lis2dw12.h:164
virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len)=0
void set_offset(float offset_x, float offset_y, float offset_z)
Definition lis2dw12.cpp:338
void handle_event_(Event event, bool active, uint32_t now, LazyCallbackManager< void()> &callback)
Definition lis2dw12.cpp:257
LazyCallbackManager< void()> tap_callback_
Definition lis2dw12.h:179
LazyCallbackManager< void()> freefall_callback_
Definition lis2dw12.h:181
LazyCallbackManager< void()> active_callback_
Definition lis2dw12.h:182
void set_transform(bool mirror_x, bool mirror_y, bool mirror_z, bool swap_xy)
Definition lis2dw12.cpp:344
bool read_acceleration_data_(float &x, float &y, float &z)
Definition lis2dw12.cpp:211
float position
Definition cover.h:0
bool state
Definition fan.h:2
bool z
Definition msa3xx.h:1
Range range
Definition msa3xx.h:0
ESPHOME_LOG_TAG(TAG, "lis2dw12")
const void * src
Definition hal.h:68
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:980
void HOT delay(uint32_t ms)
Definition hal.cpp:85
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6