mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 02:02:51 +00:00
[lis2dw12] Add ST LIS2DW12 accelerometer component (#14780)
Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
J. Nick Koston
J. Nick Koston
parent
5a3bee638f
commit
833c6634ad
@@ -311,6 +311,9 @@ esphome/components/libretiny_pwm/* @kuba2k2
|
||||
esphome/components/light/* @esphome/core
|
||||
esphome/components/lightwaverf/* @max246
|
||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||
esphome/components/lis2dw12/* @latonita
|
||||
esphome/components/lis2dw12_i2c/* @latonita
|
||||
esphome/components/lis2dw12_spi/* @latonita
|
||||
esphome/components/lm75b/* @beormund
|
||||
esphome/components/ln882h_ble/* @Bl00d-B0b
|
||||
esphome/components/ln882h_ble_tracker/* @Bl00d-B0b
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_CALIBRATION,
|
||||
CONF_ID,
|
||||
CONF_MIRROR_X,
|
||||
CONF_MIRROR_Y,
|
||||
CONF_OFFSET_X,
|
||||
CONF_OFFSET_Y,
|
||||
CONF_OFFSET_Z,
|
||||
CONF_POWER_MODE,
|
||||
CONF_RANGE,
|
||||
CONF_SWAP_XY,
|
||||
CONF_TRANSFORM,
|
||||
)
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
DOMAIN = "lis2dw12"
|
||||
|
||||
CONF_LIS2DW12_ID = "lis2dw12_id"
|
||||
|
||||
CONF_MIRROR_Z = "mirror_z"
|
||||
CONF_ON_ACTIVE = "on_active"
|
||||
CONF_ON_DOUBLE_TAP = "on_double_tap"
|
||||
CONF_ON_FREEFALL = "on_freefall"
|
||||
CONF_ON_ORIENTATION = "on_orientation"
|
||||
CONF_ON_TAP = "on_tap"
|
||||
|
||||
CONF_LOW_NOISE = "low_noise"
|
||||
CONF_FILTER_BANDWIDTH = "filter_bandwidth"
|
||||
CONF_OUTPUT_DATA_RATE = "output_data_rate"
|
||||
|
||||
lis2dw12_ns = cg.esphome_ns.namespace("lis2dw12")
|
||||
LIS2DW12Component = lis2dw12_ns.class_("LIS2DW12Component", cg.PollingComponent)
|
||||
|
||||
LIS2DW12Range = lis2dw12_ns.enum("Range", True)
|
||||
RANGES = {
|
||||
"2G": LIS2DW12Range.RANGE_2G,
|
||||
"4G": LIS2DW12Range.RANGE_4G,
|
||||
"8G": LIS2DW12Range.RANGE_8G,
|
||||
"16G": LIS2DW12Range.RANGE_16G,
|
||||
}
|
||||
|
||||
LIS2DW12PowerMode = lis2dw12_ns.enum("PowerMode", True)
|
||||
POWER_MODES = {
|
||||
"high_performance": LIS2DW12PowerMode.HIGH_PERF,
|
||||
"low_power_1": LIS2DW12PowerMode.LOW_POWER_1,
|
||||
"low_power_2": LIS2DW12PowerMode.LOW_POWER_2,
|
||||
"low_power_3": LIS2DW12PowerMode.LOW_POWER_3,
|
||||
"low_power_4": LIS2DW12PowerMode.LOW_POWER_4,
|
||||
}
|
||||
|
||||
LIS2DW12DataRate = lis2dw12_ns.enum("DataRate", True)
|
||||
DATA_RATES = {
|
||||
"1.6Hz": LIS2DW12DataRate.RATE_1_6HZ,
|
||||
"12.5Hz": LIS2DW12DataRate.RATE_12_5HZ,
|
||||
"25Hz": LIS2DW12DataRate.RATE_25HZ,
|
||||
"50Hz": LIS2DW12DataRate.RATE_50HZ,
|
||||
"100Hz": LIS2DW12DataRate.RATE_100HZ,
|
||||
"200Hz": LIS2DW12DataRate.RATE_200HZ,
|
||||
"400Hz": LIS2DW12DataRate.RATE_400HZ,
|
||||
"800Hz": LIS2DW12DataRate.RATE_800HZ,
|
||||
"1600Hz": LIS2DW12DataRate.RATE_1600HZ,
|
||||
}
|
||||
|
||||
LIS2DW12FilterBandwidth = lis2dw12_ns.enum("FilterBandwidth", True)
|
||||
FILTER_BANDWIDTHS = {
|
||||
"odr_div_2": LIS2DW12FilterBandwidth.BW_ODR_DIV_2,
|
||||
"odr_div_4": LIS2DW12FilterBandwidth.BW_ODR_DIV_4,
|
||||
"odr_div_10": LIS2DW12FilterBandwidth.BW_ODR_DIV_10,
|
||||
"odr_div_20": LIS2DW12FilterBandwidth.BW_ODR_DIV_20,
|
||||
}
|
||||
|
||||
_CALLBACK_AUTOMATIONS = (
|
||||
automation.CallbackAutomation(CONF_ON_TAP, "add_on_tap_callback"),
|
||||
automation.CallbackAutomation(CONF_ON_DOUBLE_TAP, "add_on_double_tap_callback"),
|
||||
automation.CallbackAutomation(CONF_ON_FREEFALL, "add_on_freefall_callback"),
|
||||
automation.CallbackAutomation(CONF_ON_ACTIVE, "add_on_active_callback"),
|
||||
automation.CallbackAutomation(CONF_ON_ORIENTATION, "add_on_orientation_callback"),
|
||||
)
|
||||
|
||||
_OFFSET = cv.float_range(min=-4.5, max=4.5)
|
||||
|
||||
# The ODR code means different rates per power mode: 0001 is 1.6 Hz only in low-power modes
|
||||
# (12.5 Hz in high performance) and every code above 200 Hz runs at 200 Hz in low-power modes
|
||||
_LOW_POWER_ONLY_RATES = ("1.6Hz",)
|
||||
_HIGH_PERF_ONLY_RATES = ("400Hz", "800Hz", "1600Hz")
|
||||
|
||||
|
||||
def validate_data_rate(config: ConfigType) -> ConfigType:
|
||||
"""Reject rates the chip cannot run in the chosen power mode; apply after extending the base schema."""
|
||||
rate = str(config[CONF_OUTPUT_DATA_RATE])
|
||||
mode = str(config[CONF_POWER_MODE])
|
||||
high_perf = mode == "high_performance"
|
||||
if (high_perf and rate in _LOW_POWER_ONLY_RATES) or (
|
||||
not high_perf and rate in _HIGH_PERF_ONLY_RATES
|
||||
):
|
||||
raise cv.Invalid(
|
||||
f"{CONF_OUTPUT_DATA_RATE} {rate} is not available with {CONF_POWER_MODE} {mode}",
|
||||
[CONF_OUTPUT_DATA_RATE],
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA_BASE = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LIS2DW12Component),
|
||||
cv.Optional(CONF_RANGE, default="2G"): cv.enum(RANGES, upper=True),
|
||||
cv.Optional(CONF_POWER_MODE, default="high_performance"): cv.enum(
|
||||
POWER_MODES, lower=True
|
||||
),
|
||||
cv.Optional(CONF_OUTPUT_DATA_RATE, default="100Hz"): cv.enum(DATA_RATES),
|
||||
cv.Optional(CONF_FILTER_BANDWIDTH, default="odr_div_2"): cv.enum(
|
||||
FILTER_BANDWIDTHS, lower=True
|
||||
),
|
||||
cv.Optional(CONF_LOW_NOISE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_CALIBRATION): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_OFFSET_X, default=0): _OFFSET,
|
||||
cv.Optional(CONF_OFFSET_Y, default=0): _OFFSET,
|
||||
cv.Optional(CONF_OFFSET_Z, default=0): _OFFSET,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_TRANSFORM): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_MIRROR_X, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MIRROR_Z, default=False): cv.boolean,
|
||||
cv.Optional(CONF_SWAP_XY, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_TAP): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_DOUBLE_TAP): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_FREEFALL): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ACTIVE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ORIENTATION): automation.validate_automation({}),
|
||||
}
|
||||
).extend(cv.polling_component_schema("10s"))
|
||||
|
||||
LIS2DW12_SENSOR_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LIS2DW12_ID): cv.use_id(LIS2DW12Component),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code_base(config: ConfigType) -> MockObj:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_range(config[CONF_RANGE]))
|
||||
cg.add(var.set_power_mode(config[CONF_POWER_MODE]))
|
||||
cg.add(var.set_output_data_rate(config[CONF_OUTPUT_DATA_RATE]))
|
||||
cg.add(var.set_filter_bandwidth(config[CONF_FILTER_BANDWIDTH]))
|
||||
cg.add(var.set_low_noise(config[CONF_LOW_NOISE]))
|
||||
|
||||
if transform := config.get(CONF_TRANSFORM):
|
||||
cg.add(
|
||||
var.set_transform(
|
||||
transform[CONF_MIRROR_X],
|
||||
transform[CONF_MIRROR_Y],
|
||||
transform[CONF_MIRROR_Z],
|
||||
transform[CONF_SWAP_XY],
|
||||
)
|
||||
)
|
||||
|
||||
if calibration_config := config.get(CONF_CALIBRATION):
|
||||
cg.add(
|
||||
var.set_offset(
|
||||
calibration_config[CONF_OFFSET_X],
|
||||
calibration_config[CONF_OFFSET_Y],
|
||||
calibration_config[CONF_OFFSET_Z],
|
||||
)
|
||||
)
|
||||
|
||||
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
|
||||
|
||||
return var
|
||||
@@ -0,0 +1,43 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ACTIVE, CONF_NAME, DEVICE_CLASS_VIBRATION, ICON_VIBRATE
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import CONF_LIS2DW12_ID, LIS2DW12_SENSOR_SCHEMA
|
||||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
DEPENDENCIES = ["lis2dw12"]
|
||||
|
||||
CONF_DOUBLE_TAP = "double_tap"
|
||||
CONF_FREEFALL = "freefall"
|
||||
CONF_TAP = "tap"
|
||||
|
||||
ICON_DOUBLE_TAP = "mdi:gesture-double-tap"
|
||||
ICON_FREEFALL = "mdi:parachute"
|
||||
ICON_TAP = "mdi:gesture-tap"
|
||||
|
||||
EVENT_SENSORS = (CONF_TAP, CONF_DOUBLE_TAP, CONF_FREEFALL, CONF_ACTIVE)
|
||||
ICONS = (ICON_TAP, ICON_DOUBLE_TAP, ICON_FREEFALL, ICON_VIBRATE)
|
||||
|
||||
CONFIG_SCHEMA = LIS2DW12_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(event): cv.maybe_simple_value(
|
||||
binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_VIBRATION,
|
||||
icon=icon,
|
||||
),
|
||||
key=CONF_NAME,
|
||||
)
|
||||
for event, icon in zip(EVENT_SENSORS, ICONS, strict=True)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LIS2DW12_ID])
|
||||
sensors = binary_sensor.sub_binary_sensors(config)
|
||||
await sensors(CONF_TAP, hub.set_tap_binary_sensor)
|
||||
await sensors(CONF_DOUBLE_TAP, hub.set_double_tap_binary_sensor)
|
||||
await sensors(CONF_FREEFALL, hub.set_freefall_binary_sensor)
|
||||
await sensors(CONF_ACTIVE, hub.set_active_binary_sensor)
|
||||
@@ -0,0 +1,350 @@
|
||||
#include "lis2dw12.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::lis2dw12 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "lis2dw12");
|
||||
|
||||
static constexpr uint8_t LIS2DW12_WHO_AM_I_VALUE = 0x44;
|
||||
static constexpr float GRAVITY_EARTH = 9.80665f;
|
||||
static constexpr uint32_t EVENT_COOLDOWN_MS = 500;
|
||||
static constexpr uint32_t POLL_INTERVAL_MS = 50;
|
||||
static constexpr uint8_t SIXD_POSITION_MASK = 0x3F; // XL..ZH, leaving out D6D_IA
|
||||
|
||||
static const LogString *power_mode_to_string(PowerMode mode) {
|
||||
switch (mode) {
|
||||
case PowerMode::HIGH_PERF:
|
||||
return LOG_STR("High Performance");
|
||||
case PowerMode::LOW_POWER_1:
|
||||
return LOG_STR("Low Power 1 (12-bit)");
|
||||
case PowerMode::LOW_POWER_2:
|
||||
return LOG_STR("Low Power 2");
|
||||
case PowerMode::LOW_POWER_3:
|
||||
return LOG_STR("Low Power 3");
|
||||
case PowerMode::LOW_POWER_4:
|
||||
return LOG_STR("Low Power 4");
|
||||
}
|
||||
return LOG_STR("Unknown");
|
||||
}
|
||||
|
||||
static const LogString *range_to_string(Range range) {
|
||||
switch (range) {
|
||||
case Range::RANGE_2G:
|
||||
return LOG_STR("±2g");
|
||||
case Range::RANGE_4G:
|
||||
return LOG_STR("±4g");
|
||||
case Range::RANGE_8G:
|
||||
return LOG_STR("±8g");
|
||||
case Range::RANGE_16G:
|
||||
return LOG_STR("±16g");
|
||||
}
|
||||
return LOG_STR("Unknown");
|
||||
}
|
||||
|
||||
static const LogString *data_rate_to_string(DataRate rate) {
|
||||
switch (rate) {
|
||||
case DataRate::RATE_1_6HZ:
|
||||
return LOG_STR("1.6 Hz");
|
||||
case DataRate::RATE_12_5HZ:
|
||||
return LOG_STR("12.5 Hz");
|
||||
case DataRate::RATE_25HZ:
|
||||
return LOG_STR("25 Hz");
|
||||
case DataRate::RATE_50HZ:
|
||||
return LOG_STR("50 Hz");
|
||||
case DataRate::RATE_100HZ:
|
||||
return LOG_STR("100 Hz");
|
||||
case DataRate::RATE_200HZ:
|
||||
return LOG_STR("200 Hz");
|
||||
case DataRate::RATE_400HZ:
|
||||
return LOG_STR("400 Hz");
|
||||
case DataRate::RATE_800HZ:
|
||||
return LOG_STR("800 Hz");
|
||||
case DataRate::RATE_1600HZ:
|
||||
return LOG_STR("1600 Hz");
|
||||
}
|
||||
return LOG_STR("Unknown");
|
||||
}
|
||||
|
||||
static const LogString *filter_bandwidth_to_string(FilterBandwidth bw) {
|
||||
switch (bw) {
|
||||
case FilterBandwidth::BW_ODR_DIV_2:
|
||||
return LOG_STR("ODR/2");
|
||||
case FilterBandwidth::BW_ODR_DIV_4:
|
||||
return LOG_STR("ODR/4");
|
||||
case FilterBandwidth::BW_ODR_DIV_10:
|
||||
return LOG_STR("ODR/10");
|
||||
case FilterBandwidth::BW_ODR_DIV_20:
|
||||
return LOG_STR("ODR/20");
|
||||
}
|
||||
return LOG_STR("Unknown");
|
||||
}
|
||||
|
||||
void LIS2DW12Component::setup() {
|
||||
uint8_t who_am_i{0};
|
||||
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::WHO_AM_I), &who_am_i) || who_am_i != LIS2DW12_WHO_AM_I_VALUE) {
|
||||
ESP_LOGE(TAG, "WHO_AM_I check failed. Got 0x%02X, expected 0x%02X", who_am_i, LIS2DW12_WHO_AM_I_VALUE);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Soft reset
|
||||
if (!this->write_reg_(RegisterMap::CTRL2, 0x40)) {
|
||||
this->mark_failed(LOG_STR("Soft reset failed"));
|
||||
return;
|
||||
}
|
||||
delay(1); // NOLINT - wait for reset to complete
|
||||
|
||||
uint8_t ctrl2{0};
|
||||
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::CTRL2), &ctrl2) || (ctrl2 & 0x40)) {
|
||||
this->mark_failed(LOG_STR("Soft reset did not complete"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->configure_registers_()) {
|
||||
this->mark_failed(LOG_STR("Register configuration failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 0.244 mg/LSB at ±2g (14-bit), doubling per range step; 12-bit LP1 is 4x coarser
|
||||
const bool lp1 = this->power_mode_ == PowerMode::LOW_POWER_1;
|
||||
const uint8_t range_idx = static_cast<uint8_t>(this->range_);
|
||||
const float mg = 0.244f * (1u << range_idx) * (lp1 ? 4 : 1);
|
||||
this->scale_ = mg * 0.001f * GRAVITY_EARTH;
|
||||
this->shift_ = lp1 ? 4 : 2;
|
||||
|
||||
bool events_used = !this->tap_callback_.empty() || !this->double_tap_callback_.empty() ||
|
||||
!this->freefall_callback_.empty() || !this->active_callback_.empty() ||
|
||||
!this->orientation_callback_.empty();
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
for (auto *sensor : this->event_binary_sensors_) {
|
||||
if (sensor != nullptr) {
|
||||
sensor->publish_initial_state(false);
|
||||
events_used = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
RegSixdSrc sixd;
|
||||
if (this->orientation_text_sensor_ != nullptr &&
|
||||
this->read_byte(static_cast<uint8_t>(RegisterMap::SIXD_SRC), &sixd.raw)) {
|
||||
this->publish_orientation_(sixd);
|
||||
events_used = true;
|
||||
}
|
||||
#endif
|
||||
if (!events_used) {
|
||||
this->disable_loop(); // nothing consumes the events, so skip polling the chip
|
||||
}
|
||||
}
|
||||
|
||||
bool LIS2DW12Component::configure_registers_() {
|
||||
// CTRL1: ODR[7:4], MODE[3:2] (00=low power, 01=high performance), LP_MODE[1:0] (LP1..LP4)
|
||||
const uint8_t odr_bits = static_cast<uint8_t>(this->data_rate_) << 4;
|
||||
uint8_t ctrl1;
|
||||
if (this->power_mode_ == PowerMode::HIGH_PERF) {
|
||||
ctrl1 = odr_bits | 0x04;
|
||||
} else {
|
||||
ctrl1 = odr_bits | ((static_cast<uint8_t>(this->power_mode_) - 1) & 0x03);
|
||||
}
|
||||
|
||||
// CTRL6: BW_FILT[7:6], FS[5:4], LOW_NOISE[2]
|
||||
uint8_t ctrl6 = (static_cast<uint8_t>(this->filter_bandwidth_) << 6) | (static_cast<uint8_t>(this->range_) << 4);
|
||||
if (this->low_noise_) {
|
||||
ctrl6 |= 0x04;
|
||||
}
|
||||
|
||||
return this->write_reg_(RegisterMap::CTRL2, 0x0C) && // BDU, IF_ADD_INC
|
||||
this->write_reg_(RegisterMap::CTRL1, ctrl1) && // ODR and power mode
|
||||
this->write_reg_(RegisterMap::CTRL6, ctrl6) && // bandwidth, range, low noise
|
||||
this->write_reg_(RegisterMap::TAP_THS_X, 0x29) && // 6D_THS=70 degrees, TAP_THSX=9
|
||||
this->write_reg_(RegisterMap::TAP_THS_Y, 0x09) && // TAP_THSY=9
|
||||
this->write_reg_(RegisterMap::TAP_THS_Z, 0xE9) && // TAP_THSZ=9, tap on X/Y/Z
|
||||
this->write_reg_(RegisterMap::INT_DUR, 0x76) && // LATENCY=7, QUIET=1, SHOCK=2
|
||||
this->write_reg_(RegisterMap::WAKE_UP_THS, 0x82) && // double tap enabled, WU_THS=2
|
||||
this->write_reg_(RegisterMap::WAKE_UP_DUR, 0x00) && // no wake/sleep durations
|
||||
this->write_reg_(RegisterMap::FREE_FALL, 0x33) && // FF_DUR=6, FF_THS=312 mg
|
||||
this->write_reg_(RegisterMap::CTRL4_INT1, 0xF8) && // 6D, single tap, wake-up, free-fall, double tap to INT1
|
||||
this->write_reg_(RegisterMap::CTRL3, 0x10) && // LIR: latched interrupts
|
||||
this->write_reg_(RegisterMap::CTRL7, 0x20); // INTERRUPTS_ENABLE
|
||||
}
|
||||
|
||||
void LIS2DW12Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "LIS2DW12:");
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Power Mode: %s\n"
|
||||
" Data Rate: %s\n"
|
||||
" Range: %s\n"
|
||||
" Filter Bandwidth: %s\n"
|
||||
" Low Noise: %s\n"
|
||||
" Offsets: {%.3f m/s², %.3f m/s², %.3f m/s²}\n"
|
||||
" Transform: {mirror_x=%s, mirror_y=%s, mirror_z=%s, swap_xy=%s}",
|
||||
LOG_STR_ARG(power_mode_to_string(this->power_mode_)),
|
||||
LOG_STR_ARG(data_rate_to_string(this->data_rate_)), LOG_STR_ARG(range_to_string(this->range_)),
|
||||
LOG_STR_ARG(filter_bandwidth_to_string(this->filter_bandwidth_)), YESNO(this->low_noise_),
|
||||
this->offset_x_, this->offset_y_, this->offset_z_, YESNO(this->mirror_x_), YESNO(this->mirror_y_),
|
||||
YESNO(this->mirror_z_), YESNO(this->swap_xy_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "Tap", this->event_binary_sensors_[EVENT_TAP]);
|
||||
LOG_BINARY_SENSOR(" ", "Double Tap", this->event_binary_sensors_[EVENT_DOUBLE_TAP]);
|
||||
LOG_BINARY_SENSOR(" ", "Freefall", this->event_binary_sensors_[EVENT_FREEFALL]);
|
||||
LOG_BINARY_SENSOR(" ", "Active", this->event_binary_sensors_[EVENT_ACTIVE]);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "Acceleration X", this->acceleration_x_sensor_);
|
||||
LOG_SENSOR(" ", "Acceleration Y", this->acceleration_y_sensor_);
|
||||
LOG_SENSOR(" ", "Acceleration Z", this->acceleration_z_sensor_);
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
LOG_TEXT_SENSOR(" ", "Orientation", this->orientation_text_sensor_);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LIS2DW12Component::read_acceleration_data_(float &x, float &y, float &z) {
|
||||
uint8_t accel_data[6];
|
||||
if (!this->read_bytes(static_cast<uint8_t>(RegisterMap::OUT_X_L), accel_data, 6)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t raw_x = static_cast<int16_t>(encode_uint16(accel_data[1], accel_data[0])) >> this->shift_;
|
||||
int16_t raw_y = static_cast<int16_t>(encode_uint16(accel_data[3], accel_data[2])) >> this->shift_;
|
||||
int16_t raw_z = static_cast<int16_t>(encode_uint16(accel_data[5], accel_data[4])) >> this->shift_;
|
||||
|
||||
if (this->swap_xy_) {
|
||||
std::swap(raw_x, raw_y);
|
||||
}
|
||||
x = (this->mirror_x_ ? -raw_x : raw_x) * this->scale_ + this->offset_x_;
|
||||
y = (this->mirror_y_ ? -raw_y : raw_y) * this->scale_ + this->offset_y_;
|
||||
z = (this->mirror_z_ ? -raw_z : raw_z) * this->scale_ + this->offset_z_;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
static const char *orientation_to_string(RegSixdSrc sixd) {
|
||||
if (sixd.xh)
|
||||
return "X Up";
|
||||
if (sixd.xl)
|
||||
return "X Down";
|
||||
if (sixd.yh)
|
||||
return "Y Up";
|
||||
if (sixd.yl)
|
||||
return "Y Down";
|
||||
if (sixd.zh)
|
||||
return "Z Up";
|
||||
if (sixd.zl)
|
||||
return "Z Down";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void LIS2DW12Component::publish_orientation_(RegSixdSrc sixd) {
|
||||
const uint8_t position = sixd.raw & SIXD_POSITION_MASK;
|
||||
if (this->orientation_text_sensor_ == nullptr || position == this->sixd_last_) {
|
||||
return;
|
||||
}
|
||||
this->sixd_last_ = position;
|
||||
this->orientation_text_sensor_->publish_state(orientation_to_string(sixd));
|
||||
}
|
||||
#endif
|
||||
|
||||
void LIS2DW12Component::handle_event_(Event event, bool active, uint32_t now, LazyCallbackManager<void()> &callback) {
|
||||
const uint8_t bit = 1 << event;
|
||||
bool state;
|
||||
if (active) {
|
||||
if (now - this->last_event_ms_[event] <= EVENT_COOLDOWN_MS) {
|
||||
return;
|
||||
}
|
||||
this->last_event_ms_[event] = now;
|
||||
callback.call();
|
||||
if (this->events_on_ & bit) {
|
||||
return;
|
||||
}
|
||||
this->events_on_ |= bit;
|
||||
state = true;
|
||||
} else {
|
||||
if (!(this->events_on_ & bit) || now - this->last_event_ms_[event] <= EVENT_COOLDOWN_MS) {
|
||||
return;
|
||||
}
|
||||
this->events_on_ &= ~bit;
|
||||
state = false;
|
||||
}
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->event_binary_sensors_[event] != nullptr) {
|
||||
this->event_binary_sensors_[event]->publish_state(state);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void LIS2DW12Component::loop() {
|
||||
const uint32_t now = App.get_loop_component_start_time();
|
||||
if (now - this->last_poll_ms_ < POLL_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
this->last_poll_ms_ = now;
|
||||
|
||||
// SIXD_SRC and ALL_INT_SRC are adjacent, so one read captures both before any latch clears
|
||||
uint8_t regs[2];
|
||||
if (!this->read_bytes(static_cast<uint8_t>(RegisterMap::SIXD_SRC), regs, sizeof(regs))) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
this->status_clear_warning();
|
||||
RegSixdSrc sixd;
|
||||
sixd.raw = regs[0];
|
||||
RegAllIntSrc src;
|
||||
src.raw = regs[1];
|
||||
|
||||
this->handle_event_(EVENT_TAP, src.single_tap, now, this->tap_callback_);
|
||||
this->handle_event_(EVENT_DOUBLE_TAP, src.double_tap, now, this->double_tap_callback_);
|
||||
this->handle_event_(EVENT_FREEFALL, src.ff_ia, now, this->freefall_callback_);
|
||||
this->handle_event_(EVENT_ACTIVE, src.wu_ia, now, this->active_callback_);
|
||||
|
||||
if (sixd.d6d_ia) {
|
||||
this->orientation_callback_.call();
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
this->publish_orientation_(sixd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void LIS2DW12Component::update() {
|
||||
float x, y, z;
|
||||
if (!this->read_acceleration_data_(x, y, z)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Acceleration: {x = %+1.3f m/s², y = %+1.3f m/s², z = %+1.3f m/s²}", x, y, z);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->acceleration_x_sensor_ != nullptr)
|
||||
this->acceleration_x_sensor_->publish_state(x);
|
||||
if (this->acceleration_y_sensor_ != nullptr)
|
||||
this->acceleration_y_sensor_->publish_state(y);
|
||||
if (this->acceleration_z_sensor_ != nullptr)
|
||||
this->acceleration_z_sensor_->publish_state(z);
|
||||
#endif
|
||||
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
void LIS2DW12Component::set_offset(float offset_x, float offset_y, float offset_z) {
|
||||
this->offset_x_ = offset_x;
|
||||
this->offset_y_ = offset_y;
|
||||
this->offset_z_ = offset_z;
|
||||
}
|
||||
|
||||
void LIS2DW12Component::set_transform(bool mirror_x, bool mirror_y, bool mirror_z, bool swap_xy) {
|
||||
this->mirror_x_ = mirror_x;
|
||||
this->mirror_y_ = mirror_y;
|
||||
this->mirror_z_ = mirror_z;
|
||||
this->swap_xy_ = swap_xy;
|
||||
}
|
||||
|
||||
} // namespace esphome::lis2dw12
|
||||
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::lis2dw12 {
|
||||
|
||||
// LIS2DW12 Register Map
|
||||
enum class RegisterMap : uint8_t {
|
||||
WHO_AM_I = 0x0F,
|
||||
CTRL1 = 0x20,
|
||||
CTRL2 = 0x21,
|
||||
CTRL3 = 0x22,
|
||||
CTRL4_INT1 = 0x23,
|
||||
CTRL6 = 0x25,
|
||||
OUT_X_L = 0x28,
|
||||
TAP_THS_X = 0x30,
|
||||
TAP_THS_Y = 0x31,
|
||||
TAP_THS_Z = 0x32,
|
||||
INT_DUR = 0x33,
|
||||
WAKE_UP_THS = 0x34,
|
||||
WAKE_UP_DUR = 0x35,
|
||||
FREE_FALL = 0x36,
|
||||
SIXD_SRC = 0x3A,
|
||||
ALL_INT_SRC = 0x3B,
|
||||
CTRL7 = 0x3F,
|
||||
};
|
||||
|
||||
enum class Range : uint8_t {
|
||||
RANGE_2G = 0b00,
|
||||
RANGE_4G = 0b01,
|
||||
RANGE_8G = 0b10,
|
||||
RANGE_16G = 0b11,
|
||||
};
|
||||
|
||||
enum class PowerMode : uint8_t {
|
||||
HIGH_PERF = 0,
|
||||
LOW_POWER_1 = 1,
|
||||
LOW_POWER_2 = 2,
|
||||
LOW_POWER_3 = 3,
|
||||
LOW_POWER_4 = 4,
|
||||
};
|
||||
|
||||
enum class DataRate : uint8_t {
|
||||
RATE_1_6HZ = 0b0001,
|
||||
RATE_12_5HZ = 0b0010,
|
||||
RATE_25HZ = 0b0011,
|
||||
RATE_50HZ = 0b0100,
|
||||
RATE_100HZ = 0b0101,
|
||||
RATE_200HZ = 0b0110,
|
||||
RATE_400HZ = 0b0111,
|
||||
RATE_800HZ = 0b1000,
|
||||
RATE_1600HZ = 0b1001,
|
||||
};
|
||||
|
||||
enum class FilterBandwidth : uint8_t {
|
||||
BW_ODR_DIV_2 = 0b00,
|
||||
BW_ODR_DIV_4 = 0b01,
|
||||
BW_ODR_DIV_10 = 0b10,
|
||||
BW_ODR_DIV_20 = 0b11,
|
||||
};
|
||||
|
||||
// ALL_INT_SRC register (0x3B)
|
||||
union RegAllIntSrc {
|
||||
struct {
|
||||
bool ff_ia : 1;
|
||||
bool wu_ia : 1;
|
||||
bool single_tap : 1;
|
||||
bool double_tap : 1;
|
||||
bool d6d_ia : 1;
|
||||
bool sleep_change_ia : 1;
|
||||
uint8_t reserved : 2;
|
||||
} __attribute__((packed));
|
||||
uint8_t raw{0};
|
||||
};
|
||||
|
||||
// SIXD_SRC register (0x3A)
|
||||
union RegSixdSrc {
|
||||
struct {
|
||||
bool xl : 1;
|
||||
bool xh : 1;
|
||||
bool yl : 1;
|
||||
bool yh : 1;
|
||||
bool zl : 1;
|
||||
bool zh : 1;
|
||||
bool d6d_ia : 1;
|
||||
uint8_t reserved : 1;
|
||||
} __attribute__((packed));
|
||||
uint8_t raw{0};
|
||||
};
|
||||
|
||||
// Debounced events, also the bit index into events_on_
|
||||
enum Event : uint8_t {
|
||||
EVENT_TAP = 0,
|
||||
EVENT_DOUBLE_TAP,
|
||||
EVENT_FREEFALL,
|
||||
EVENT_ACTIVE,
|
||||
EVENT_COUNT,
|
||||
};
|
||||
|
||||
class LIS2DW12Component : public PollingComponent {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
void update() override;
|
||||
|
||||
void set_power_mode(PowerMode mode) { this->power_mode_ = mode; }
|
||||
void set_low_noise(bool low_noise) { this->low_noise_ = low_noise; }
|
||||
void set_output_data_rate(DataRate rate) { this->data_rate_ = rate; }
|
||||
void set_range(Range range) { this->range_ = range; }
|
||||
void set_filter_bandwidth(FilterBandwidth bw) { this->filter_bandwidth_ = bw; }
|
||||
void set_offset(float offset_x, float offset_y, float offset_z);
|
||||
void set_transform(bool mirror_x, bool mirror_y, bool mirror_z, bool swap_xy);
|
||||
|
||||
bool read_byte(uint8_t a_register, uint8_t *data) { return this->read_bytes(a_register, data, 1); }
|
||||
virtual bool write_byte(uint8_t a_register, uint8_t data) = 0;
|
||||
virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void set_tap_binary_sensor(binary_sensor::BinarySensor *s) { this->event_binary_sensors_[EVENT_TAP] = s; }
|
||||
void set_double_tap_binary_sensor(binary_sensor::BinarySensor *s) {
|
||||
this->event_binary_sensors_[EVENT_DOUBLE_TAP] = s;
|
||||
}
|
||||
void set_freefall_binary_sensor(binary_sensor::BinarySensor *s) { this->event_binary_sensors_[EVENT_FREEFALL] = s; }
|
||||
void set_active_binary_sensor(binary_sensor::BinarySensor *s) { this->event_binary_sensors_[EVENT_ACTIVE] = s; }
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
SUB_SENSOR(acceleration_x)
|
||||
SUB_SENSOR(acceleration_y)
|
||||
SUB_SENSOR(acceleration_z)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
SUB_TEXT_SENSOR(orientation)
|
||||
#endif
|
||||
|
||||
template<typename F> void add_on_tap_callback(F &&callback) { this->tap_callback_.add(std::forward<F>(callback)); }
|
||||
template<typename F> void add_on_double_tap_callback(F &&callback) {
|
||||
this->double_tap_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
template<typename F> void add_on_freefall_callback(F &&callback) {
|
||||
this->freefall_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
template<typename F> void add_on_active_callback(F &&callback) {
|
||||
this->active_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
template<typename F> void add_on_orientation_callback(F &&callback) {
|
||||
this->orientation_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
protected:
|
||||
bool write_reg_(RegisterMap reg, uint8_t value) { return this->write_byte(static_cast<uint8_t>(reg), value); }
|
||||
bool configure_registers_();
|
||||
bool read_acceleration_data_(float &x, float &y, float &z);
|
||||
void handle_event_(Event event, bool active, uint32_t now, LazyCallbackManager<void()> &callback);
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void publish_orientation_(RegSixdSrc sixd);
|
||||
#endif
|
||||
|
||||
float offset_x_{0}, offset_y_{0}, offset_z_{0};
|
||||
float scale_{0}; // m/s² per LSB after shift_
|
||||
uint32_t last_event_ms_[EVENT_COUNT]{};
|
||||
uint32_t last_poll_ms_{0};
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
binary_sensor::BinarySensor *event_binary_sensors_[EVENT_COUNT]{};
|
||||
#endif
|
||||
LazyCallbackManager<void()> tap_callback_;
|
||||
LazyCallbackManager<void()> double_tap_callback_;
|
||||
LazyCallbackManager<void()> freefall_callback_;
|
||||
LazyCallbackManager<void()> active_callback_;
|
||||
LazyCallbackManager<void()> orientation_callback_;
|
||||
|
||||
PowerMode power_mode_{PowerMode::HIGH_PERF};
|
||||
DataRate data_rate_{DataRate::RATE_100HZ};
|
||||
Range range_{Range::RANGE_2G};
|
||||
FilterBandwidth filter_bandwidth_{FilterBandwidth::BW_ODR_DIV_2};
|
||||
bool low_noise_{false};
|
||||
bool mirror_x_{false}, mirror_y_{false}, mirror_z_{false}, swap_xy_{false};
|
||||
uint8_t shift_{2}; // data is left-justified: 14-bit >> 2, 12-bit (LP1) >> 4
|
||||
uint8_t events_on_{0}; // bit per Event: binary sensor currently published on
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
uint8_t sixd_last_{0xFF}; // position bits only; 0xFF cannot occur, so the first read publishes
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome::lis2dw12
|
||||
@@ -0,0 +1,43 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ACCELERATION_X,
|
||||
CONF_ACCELERATION_Y,
|
||||
CONF_ACCELERATION_Z,
|
||||
CONF_NAME,
|
||||
ICON_ACCELERATION,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_METER_PER_SECOND_SQUARED,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import CONF_LIS2DW12_ID, LIS2DW12_SENSOR_SCHEMA
|
||||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
DEPENDENCIES = ["lis2dw12"]
|
||||
|
||||
ACCELERATION_SENSORS = (CONF_ACCELERATION_X, CONF_ACCELERATION_Y, CONF_ACCELERATION_Z)
|
||||
|
||||
accel_schema = cv.maybe_simple_value(
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_ACCELERATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
key=CONF_NAME,
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = LIS2DW12_SENSOR_SCHEMA.extend(
|
||||
{cv.Optional(key): accel_schema for key in ACCELERATION_SENSORS}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LIS2DW12_ID])
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_ACCELERATION_X, hub.set_acceleration_x_sensor)
|
||||
await sensors(CONF_ACCELERATION_Y, hub.set_acceleration_y_sensor)
|
||||
await sensors(CONF_ACCELERATION_Z, hub.set_acceleration_z_sensor)
|
||||
@@ -0,0 +1,27 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_NAME, ICON_SCREEN_ROTATION
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import CONF_LIS2DW12_ID, LIS2DW12_SENSOR_SCHEMA
|
||||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
DEPENDENCIES = ["lis2dw12"]
|
||||
|
||||
CONF_ORIENTATION = "orientation"
|
||||
|
||||
CONFIG_SCHEMA = LIS2DW12_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(CONF_ORIENTATION): cv.maybe_simple_value(
|
||||
text_sensor.text_sensor_schema(icon=ICON_SCREEN_ROTATION),
|
||||
key=CONF_NAME,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LIS2DW12_ID])
|
||||
text_sensors = text_sensor.sub_text_sensors(config)
|
||||
await text_sensors(CONF_ORIENTATION, hub.set_orientation_text_sensor)
|
||||
@@ -0,0 +1,35 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
from esphome.components.lis2dw12 import (
|
||||
CONFIG_SCHEMA_BASE,
|
||||
LIS2DW12Component,
|
||||
to_code_base,
|
||||
validate_data_rate,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
AUTO_LOAD = ["lis2dw12"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
DOMAIN = "lis2dw12_i2c"
|
||||
MULTI_CONF = True
|
||||
|
||||
lis2dw12_i2c_ns = cg.esphome_ns.namespace("lis2dw12_i2c")
|
||||
LIS2DW12I2CComponent = lis2dw12_i2c_ns.class_(
|
||||
"LIS2DW12I2CComponent", LIS2DW12Component, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
CONFIG_SCHEMA_BASE.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LIS2DW12I2CComponent),
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x19)),
|
||||
validate_data_rate,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = await to_code_base(config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "lis2dw12_i2c.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::lis2dw12_i2c {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "lis2dw12_i2c");
|
||||
|
||||
void LIS2DW12I2CComponent::dump_config() {
|
||||
LOG_I2C_DEVICE(this);
|
||||
lis2dw12::LIS2DW12Component::dump_config();
|
||||
}
|
||||
|
||||
} // namespace esphome::lis2dw12_i2c
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/lis2dw12/lis2dw12.h"
|
||||
|
||||
namespace esphome::lis2dw12_i2c {
|
||||
|
||||
class LIS2DW12I2CComponent : public lis2dw12::LIS2DW12Component, public i2c::I2CDevice {
|
||||
public:
|
||||
bool write_byte(uint8_t a_register, uint8_t data) override { return i2c::I2CDevice::write_byte(a_register, data); }
|
||||
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override {
|
||||
return i2c::I2CDevice::read_bytes(a_register, data, len);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::lis2dw12_i2c
|
||||
@@ -0,0 +1,37 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import spi
|
||||
from esphome.components.lis2dw12 import (
|
||||
CONFIG_SCHEMA_BASE,
|
||||
LIS2DW12Component,
|
||||
to_code_base,
|
||||
validate_data_rate,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
AUTO_LOAD = ["lis2dw12"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
DOMAIN = "lis2dw12_spi"
|
||||
MULTI_CONF = True
|
||||
|
||||
lis2dw12_spi_ns = cg.esphome_ns.namespace("lis2dw12_spi")
|
||||
LIS2DW12SPIComponent = lis2dw12_spi_ns.class_(
|
||||
"LIS2DW12SPIComponent",
|
||||
LIS2DW12Component,
|
||||
spi.SPIDevice,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
CONFIG_SCHEMA_BASE.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LIS2DW12SPIComponent),
|
||||
}
|
||||
).extend(spi.spi_device_schema(cs_pin_required=True)),
|
||||
validate_data_rate,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = await to_code_base(config)
|
||||
await spi.register_spi_device(var, config)
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "lis2dw12_spi.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::lis2dw12_spi {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "lis2dw12_spi");
|
||||
|
||||
void LIS2DW12SPIComponent::setup() {
|
||||
this->spi_setup();
|
||||
lis2dw12::LIS2DW12Component::setup();
|
||||
}
|
||||
|
||||
void LIS2DW12SPIComponent::dump_config() {
|
||||
LOG_SPI_DEVICE(this);
|
||||
lis2dw12::LIS2DW12Component::dump_config();
|
||||
}
|
||||
|
||||
bool LIS2DW12SPIComponent::write_byte(uint8_t a_register, uint8_t data) {
|
||||
this->enable();
|
||||
this->transfer_byte(a_register & 0x3F);
|
||||
this->transfer_byte(data);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LIS2DW12SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
|
||||
this->enable();
|
||||
this->transfer_byte(a_register | 0x80);
|
||||
this->read_array(data, len);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace esphome::lis2dw12_spi
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/lis2dw12/lis2dw12.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
namespace esphome::lis2dw12_spi {
|
||||
|
||||
class LIS2DW12SPIComponent : public lis2dw12::LIS2DW12Component,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
|
||||
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
bool write_byte(uint8_t a_register, uint8_t data) override;
|
||||
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::lis2dw12_spi
|
||||
@@ -0,0 +1,60 @@
|
||||
lis2dw12_i2c:
|
||||
id: lis2dw12_i2c_accel
|
||||
i2c_id: i2c_bus
|
||||
range: 4G
|
||||
power_mode: high_performance
|
||||
output_data_rate: 100Hz
|
||||
filter_bandwidth: odr_div_2
|
||||
low_noise: true
|
||||
update_interval: 10s
|
||||
calibration:
|
||||
offset_x: -0.250
|
||||
offset_y: -0.400
|
||||
offset_z: -0.800
|
||||
transform:
|
||||
mirror_x: false
|
||||
mirror_y: true
|
||||
mirror_z: true
|
||||
swap_xy: false
|
||||
on_tap:
|
||||
- then:
|
||||
- logger.log: "Tapped"
|
||||
- then:
|
||||
- logger.log: "Tapped again"
|
||||
on_double_tap:
|
||||
- then:
|
||||
- logger.log: "Double tapped"
|
||||
on_freefall:
|
||||
- then:
|
||||
- logger.log: "Freefall detected"
|
||||
on_active:
|
||||
- then:
|
||||
- logger.log: "Activity detected"
|
||||
on_orientation:
|
||||
- then:
|
||||
- logger.log: "Orientation changed"
|
||||
|
||||
sensor:
|
||||
- platform: lis2dw12
|
||||
lis2dw12_id: lis2dw12_i2c_accel
|
||||
acceleration_x: Accel X
|
||||
acceleration_y: Accel Y
|
||||
acceleration_z: Accel Z
|
||||
|
||||
text_sensor:
|
||||
- platform: lis2dw12
|
||||
lis2dw12_id: lis2dw12_i2c_accel
|
||||
orientation: Orientation
|
||||
|
||||
binary_sensor:
|
||||
- platform: lis2dw12
|
||||
lis2dw12_id: lis2dw12_i2c_accel
|
||||
tap: Single tap
|
||||
double_tap:
|
||||
name: Double tap
|
||||
freefall:
|
||||
name: Freefall
|
||||
active:
|
||||
name: Active
|
||||
filters:
|
||||
- delayed_off: 5000ms
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
lis2dw12_i2c: !include common.yaml
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
|
||||
lis2dw12_i2c: !include common.yaml
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
|
||||
lis2dw12_i2c: !include common.yaml
|
||||
@@ -0,0 +1,61 @@
|
||||
lis2dw12_spi:
|
||||
id: lis2dw12_spi_accel
|
||||
spi_id: spi_bus
|
||||
cs_pin: GPIO5
|
||||
range: 4G
|
||||
power_mode: high_performance
|
||||
output_data_rate: 100Hz
|
||||
filter_bandwidth: odr_div_2
|
||||
low_noise: true
|
||||
update_interval: 10s
|
||||
calibration:
|
||||
offset_x: -0.250
|
||||
offset_y: -0.400
|
||||
offset_z: -0.800
|
||||
transform:
|
||||
mirror_x: false
|
||||
mirror_y: true
|
||||
mirror_z: true
|
||||
swap_xy: false
|
||||
on_tap:
|
||||
- then:
|
||||
- logger.log: "Tapped"
|
||||
- then:
|
||||
- logger.log: "Tapped again"
|
||||
on_double_tap:
|
||||
- then:
|
||||
- logger.log: "Double tapped"
|
||||
on_freefall:
|
||||
- then:
|
||||
- logger.log: "Freefall detected"
|
||||
on_active:
|
||||
- then:
|
||||
- logger.log: "Activity detected"
|
||||
on_orientation:
|
||||
- then:
|
||||
- logger.log: "Orientation changed"
|
||||
|
||||
sensor:
|
||||
- platform: lis2dw12
|
||||
lis2dw12_id: lis2dw12_spi_accel
|
||||
acceleration_x: Accel X
|
||||
acceleration_y: Accel Y
|
||||
acceleration_z: Accel Z
|
||||
|
||||
text_sensor:
|
||||
- platform: lis2dw12
|
||||
lis2dw12_id: lis2dw12_spi_accel
|
||||
orientation: Orientation
|
||||
|
||||
binary_sensor:
|
||||
- platform: lis2dw12
|
||||
lis2dw12_id: lis2dw12_spi_accel
|
||||
tap: Single tap
|
||||
double_tap:
|
||||
name: Double tap
|
||||
freefall:
|
||||
name: Freefall
|
||||
active:
|
||||
name: Active
|
||||
filters:
|
||||
- delayed_off: 5000ms
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
|
||||
lis2dw12_spi: !include common.yaml
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml
|
||||
lis2dw12_spi: !include common.yaml
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml
|
||||
lis2dw12_spi: !include common.yaml
|
||||
Reference in New Issue
Block a user