[lsm6ds] Restructure component with _base, _i2c, _spi pattern
Major restructure to follow ESPHome conventions for multi-interface components: Component Structure Changes: - Renamed lsm6ds/ to lsm6ds_base/ to follow _base naming convention - Created lsm6ds_i2c/ for I2C interface implementation - Created lsm6ds_spi/ for SPI interface implementation - Both interfaces use AUTO_LOAD to automatically include base component - Updated C++ namespaces to match component folder names (lsm6ds_base, lsm6ds_i2c, lsm6ds_spi) Features: - Support for LSM6DSOX, LSM6DS3TR-C, LSM6DSO32 variants - Full 3-axis accelerometer and gyroscope support - Temperature sensor support - Configurable ranges: Accel (2G-16G), Gyro (125-2000 DPS) - Configurable output data rate (12.5Hz - 6.66kHz) - Two usage patterns: all-in-one and hub+sensor platform Implementation Details: - Base component (lsm6ds_base) contains shared code and abstract LSM6DSComponent class - I2C variant implements LSM6DSI2CDevice with I2C read/write methods - SPI variant implements LSM6DSSPIDevice with SPI read/write methods (MSB-based protocol) - Both variants support optional sensor.py for hub+sensor pattern - Added README_EXAMPLE.md files with usage examples for both interfaces - Added component test files for validation Documentation: - Added COMPONENT_STRUCTURE.md explaining architecture and usage patterns - Included example configurations for both I2C and SPI - Documented all configuration options and sensor variants
This commit is contained in:
1 parent
b9aef32d79
commit
b7b3b06e92
22 files changed
+937
-331
No files matched your search
@@ -0,0 +1,124 @@
|
||||
# LSM6DS Component Structure
|
||||
|
||||
This document describes the organization of the LSM6DS family IMU sensor components for ESPHome.
|
||||
|
||||
## Folder Structure
|
||||
|
||||
```
|
||||
esphome/components/
|
||||
├── lsm6ds_base/ # Base component (shared code and logic)
|
||||
│ ├── __init__.py # Base schema, enums, configuration, register_lsm6ds()
|
||||
│ ├── const.py # Constants: register addresses, WHO_AM_I values, CONF_* keys
|
||||
│ ├── lsm6ds.h # Base C++ class: LSM6DSComponent (abstract)
|
||||
│ └── lsm6ds.cpp # Base C++ implementation: setup(), update(), dump_config()
|
||||
│
|
||||
├── lsm6ds_i2c/ # I2C interface implementation
|
||||
│ ├── __init__.py # I2C component registration (AUTO_LOAD: lsm6ds_base)
|
||||
│ ├── sensor.py # Optional sensor platform (hub + sensor pattern)
|
||||
│ ├── lsm6ds_i2c.h # LSM6DSI2CDevice class (implements read/write via I2C)
|
||||
│ ├── lsm6ds_i2c.cpp # I2C implementation (minimal/empty)
|
||||
│ └── README_EXAMPLE.md # I2C usage examples and configuration guide
|
||||
│
|
||||
└── lsm6ds_spi/ # SPI interface implementation
|
||||
├── __init__.py # SPI component registration (AUTO_LOAD: lsm6ds_base)
|
||||
├── sensor.py # Optional sensor platform (hub + sensor pattern)
|
||||
├── lsm6ds_spi.h # LSM6DSSPIDevice class (implements read/write via SPI)
|
||||
├── lsm6ds_spi.cpp # SPI implementation (minimal/empty)
|
||||
└── README_EXAMPLE.md # SPI usage examples and configuration guide
|
||||
```
|
||||
|
||||
## Component Relationships
|
||||
|
||||
### Auto-Loading Pattern
|
||||
Both `lsm6ds_i2c` and `lsm6ds_spi` use `AUTO_LOAD = ["lsm6ds_base"]` to automatically include the base component when either interface variant is used. Users only need to configure `lsm6ds_i2c` or `lsm6ds_spi` directly.
|
||||
|
||||
### Dependencies
|
||||
- **lsm6ds_base**: No dependencies (standalone base)
|
||||
- **lsm6ds_i2c**: Depends on `i2c`, auto-loads `lsm6ds_base`
|
||||
- **lsm6ds_spi**: Depends on `spi`, auto-loads `lsm6ds_base`
|
||||
|
||||
### Class Hierarchy
|
||||
|
||||
```
|
||||
C++ Class Structure:
|
||||
|
||||
LSM6DSComponent (abstract base class)
|
||||
↑
|
||||
├── LSM6DSI2CDevice : public LSM6DSComponent, public i2c::I2CDevice
|
||||
│ └── Implements: read_register() and write_register() via I2C
|
||||
│
|
||||
└── LSM6DSSPIDevice : public LSM6DSComponent, public spi::SPIDevice
|
||||
└── Implements: read_register() and write_register() via SPI
|
||||
```
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### Pattern 1: All-in-One (Recommended for most users)
|
||||
Configure the sensor with all outputs directly in the component block:
|
||||
|
||||
```yaml
|
||||
lsm6ds_i2c: # or lsm6ds_spi
|
||||
id: imu_sensor
|
||||
# ... interface config (I2C address or SPI CS pin)
|
||||
accel_range: 4G
|
||||
gyro_range: 500DPS
|
||||
accel_odr: 104HZ
|
||||
gyro_odr: 104HZ
|
||||
accel_x:
|
||||
name: "Accel X"
|
||||
gyro_x:
|
||||
name: "Gyro X"
|
||||
# ... other sensors
|
||||
```
|
||||
|
||||
### Pattern 2: Hub + Sensor Platform
|
||||
Configure the hub separately, then add sensors under the sensor platform:
|
||||
|
||||
```yaml
|
||||
lsm6ds_i2c: # or lsm6ds_spi
|
||||
id: imu_hub
|
||||
# ... interface config
|
||||
accel_range: 4G
|
||||
gyro_range: 500DPS
|
||||
|
||||
sensor:
|
||||
- platform: lsm6ds_i2c # or lsm6ds_spi
|
||||
lsm6ds_id: imu_hub
|
||||
accel_x:
|
||||
name: "Accel X"
|
||||
# ... other sensors
|
||||
```
|
||||
|
||||
## Supported Sensors
|
||||
|
||||
- **LSM6DSOX**: WHO_AM_I = 0x6C
|
||||
- **LSM6DS3TR-C**: WHO_AM_I = 0x6A
|
||||
- **LSM6DSO32**: WHO_AM_I = 0x69
|
||||
|
||||
All variants support:
|
||||
- 3-axis accelerometer (±2G, ±4G, ±8G, ±16G)
|
||||
- 3-axis gyroscope (±125, ±250, ±500, ±1000, ±2000 DPS)
|
||||
- Temperature sensor
|
||||
- Configurable output data rate (12.5Hz to 6.66kHz)
|
||||
- Both I2C and SPI interfaces
|
||||
|
||||
## Code Generation Flow
|
||||
|
||||
1. User configures `lsm6ds_i2c` or `lsm6ds_spi` in YAML
|
||||
2. ESPHome auto-loads `lsm6ds_base` component
|
||||
3. Python `__init__.py` validates config and generates C++ code
|
||||
4. `register_lsm6ds()` function:
|
||||
- Registers component with ESPHome
|
||||
- Sets range and ODR configuration
|
||||
- Creates sensor entities for requested outputs
|
||||
5. C++ `setup()` method:
|
||||
- Detects sensor variant via WHO_AM_I
|
||||
- Configures sensor registers
|
||||
- Calculates sensitivity values
|
||||
6. C++ `update()` method:
|
||||
- Reads sensor data
|
||||
- Publishes to sensor entities
|
||||
|
||||
## CODEOWNERS
|
||||
|
||||
All three components list `@NonaSuomy` as the code owner for maintenance and review.
|
||||
@@ -1,119 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, spi
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
from .const import (
|
||||
lsm6ds_ns,
|
||||
LSM6DSComponent,
|
||||
CONF_ACCEL_RANGE,
|
||||
CONF_GYRO_RANGE,
|
||||
CONF_ACCEL_ODR,
|
||||
CONF_GYRO_ODR,
|
||||
LSM6DS_ACCEL_RANGE_2G,
|
||||
LSM6DS_ACCEL_RANGE_16G,
|
||||
LSM6DS_ACCEL_RANGE_4G,
|
||||
LSM6DS_ACCEL_RANGE_8G,
|
||||
LSM6DS_GYRO_RANGE_250DPS,
|
||||
LSM6DS_GYRO_RANGE_500DPS,
|
||||
LSM6DS_GYRO_RANGE_1000DPS,
|
||||
LSM6DS_GYRO_RANGE_2000DPS,
|
||||
LSM6DS_GYRO_RANGE_125DPS,
|
||||
LSM6DS_ACCEL_ODR_OFF,
|
||||
LSM6DS_ACCEL_ODR_12_5HZ,
|
||||
LSM6DS_ACCEL_ODR_26HZ,
|
||||
LSM6DS_ACCEL_ODR_52HZ,
|
||||
LSM6DS_ACCEL_ODR_104HZ,
|
||||
LSM6DS_ACCEL_ODR_208HZ,
|
||||
LSM6DS_ACCEL_ODR_416HZ,
|
||||
LSM6DS_ACCEL_ODR_833HZ,
|
||||
LSM6DS_ACCEL_ODR_1_66KHZ,
|
||||
LSM6DS_ACCEL_ODR_3_33KHZ,
|
||||
LSM6DS_ACCEL_ODR_6_66KHZ,
|
||||
LSM6DS_GYRO_ODR_OFF,
|
||||
LSM6DS_GYRO_ODR_12_5HZ,
|
||||
LSM6DS_GYRO_ODR_26HZ,
|
||||
LSM6DS_GYRO_ODR_52HZ,
|
||||
LSM6DS_GYRO_ODR_104HZ,
|
||||
LSM6DS_GYRO_ODR_208HZ,
|
||||
LSM6DS_GYRO_ODR_416HZ,
|
||||
LSM6DS_GYRO_ODR_833HZ,
|
||||
LSM6DS_GYRO_ODR_1_66KHZ,
|
||||
LSM6DS_GYRO_ODR_3_33KHZ,
|
||||
LSM6DS_GYRO_ODR_6_66KHZ,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@NonaSuomy"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["sensor"]
|
||||
|
||||
LSM6DSI2CDevice = lsm6ds_ns.class_("LSM6DSI2CDevice", LSM6DSComponent, i2c.I2CDevice)
|
||||
|
||||
ACCEL_RANGES = {
|
||||
"2G": LSM6DS_ACCEL_RANGE_2G,
|
||||
"16G": LSM6DS_ACCEL_RANGE_16G,
|
||||
"4G": LSM6DS_ACCEL_RANGE_4G,
|
||||
"8G": LSM6DS_ACCEL_RANGE_8G,
|
||||
}
|
||||
|
||||
GYRO_RANGES = {
|
||||
"250DPS": LSM6DS_GYRO_RANGE_250DPS,
|
||||
"500DPS": LSM6DS_GYRO_RANGE_500DPS,
|
||||
"1000DPS": LSM6DS_GYRO_RANGE_1000DPS,
|
||||
"2000DPS": LSM6DS_GYRO_RANGE_2000DPS,
|
||||
"125DPS": LSM6DS_GYRO_RANGE_125DPS,
|
||||
}
|
||||
|
||||
ACCEL_ODRS = {
|
||||
"OFF": LSM6DS_ACCEL_ODR_OFF,
|
||||
"12.5HZ": LSM6DS_ACCEL_ODR_12_5HZ,
|
||||
"26HZ": LSM6DS_ACCEL_ODR_26HZ,
|
||||
"52HZ": LSM6DS_ACCEL_ODR_52HZ,
|
||||
"104HZ": LSM6DS_ACCEL_ODR_104HZ,
|
||||
"208HZ": LSM6DS_ACCEL_ODR_208HZ,
|
||||
"416HZ": LSM6DS_ACCEL_ODR_416HZ,
|
||||
"833HZ": LSM6DS_ACCEL_ODR_833HZ,
|
||||
"1.66KHZ": LSM6DS_ACCEL_ODR_1_66KHZ,
|
||||
"3.33KHZ": LSM6DS_ACCEL_ODR_3_33KHZ,
|
||||
"6.66KHZ": LSM6DS_ACCEL_ODR_6_66KHZ,
|
||||
}
|
||||
|
||||
GYRO_ODRS = {
|
||||
"OFF": LSM6DS_GYRO_ODR_OFF,
|
||||
"12.5HZ": LSM6DS_GYRO_ODR_12_5HZ,
|
||||
"26HZ": LSM6DS_GYRO_ODR_26HZ,
|
||||
"52HZ": LSM6DS_GYRO_ODR_52HZ,
|
||||
"104HZ": LSM6DS_GYRO_ODR_104HZ,
|
||||
"208HZ": LSM6DS_GYRO_ODR_208HZ,
|
||||
"416HZ": LSM6DS_GYRO_ODR_416HZ,
|
||||
"833HZ": LSM6DS_GYRO_ODR_833HZ,
|
||||
"1.66KHZ": LSM6DS_GYRO_ODR_1_66KHZ,
|
||||
"3.33KHZ": LSM6DS_GYRO_ODR_3_33KHZ,
|
||||
"6.66KHZ": LSM6DS_GYRO_ODR_6_66KHZ,
|
||||
}
|
||||
|
||||
BASE_SCHEMA = cv.Schema({
|
||||
cv.Optional(CONF_ACCEL_RANGE, default="2G"): cv.enum(ACCEL_RANGES, upper=True),
|
||||
cv.Optional(CONF_GYRO_RANGE, default="250DPS"): cv.enum(GYRO_RANGES, upper=True),
|
||||
cv.Optional(CONF_ACCEL_ODR, default="104HZ"): cv.enum(ACCEL_ODRS, upper=True),
|
||||
cv.Optional(CONF_GYRO_ODR, default="104HZ"): cv.enum(GYRO_ODRS, upper=True),
|
||||
}).extend(cv.polling_component_schema("60s"))
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(LSM6DSI2CDevice),
|
||||
}).extend(BASE_SCHEMA).extend(i2c.i2c_device_schema(None))
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
accel_range = ACCEL_RANGES[config[CONF_ACCEL_RANGE]]
|
||||
gyro_range = GYRO_RANGES[config[CONF_GYRO_RANGE]]
|
||||
accel_odr = ACCEL_ODRS[config[CONF_ACCEL_ODR]]
|
||||
gyro_odr = GYRO_ODRS[config[CONF_GYRO_ODR]]
|
||||
|
||||
cg.add(var.set_accel_range(accel_range))
|
||||
cg.add(var.set_gyro_range(gyro_range))
|
||||
cg.add(var.set_accel_odr(accel_odr))
|
||||
cg.add(var.set_gyro_odr(gyro_odr))
|
||||
@@ -1,70 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
# Registers
|
||||
LSM6DS_WHO_AM_I = 0x0F
|
||||
LSM6DS_CTRL1_XL = 0x10
|
||||
LSM6DS_CTRL2_G = 0x11
|
||||
LSM6DS_CTRL3_C = 0x12
|
||||
LSM6DS_STATUS_REG = 0x1E
|
||||
LSM6DS_OUT_TEMP_L = 0x20
|
||||
LSM6DS_OUTX_L_G = 0x22
|
||||
LSM6DS_OUTX_L_A = 0x28
|
||||
|
||||
# WHO_AM_I Values for different variants
|
||||
LSM6DSOX_WHO_AM_I_VALUE = 0x6C
|
||||
LSM6DS3TR_WHO_AM_I_VALUE = 0x6A
|
||||
LSM6DSO32TR_WHO_AM_I_VALUE = 0x69
|
||||
|
||||
lsm6ds_ns = cg.esphome_ns.namespace("lsm6ds")
|
||||
LSM6DSComponent = lsm6ds_ns.class_("LSM6DSComponent", cg.PollingComponent)
|
||||
|
||||
LSM6DSAccelRange = lsm6ds_ns.enum("LSM6DSAccelRange")
|
||||
LSM6DS_ACCEL_RANGE_2G = LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_2G
|
||||
LSM6DS_ACCEL_RANGE_16G = LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_16G
|
||||
LSM6DS_ACCEL_RANGE_4G = LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_4G
|
||||
LSM6DS_ACCEL_RANGE_8G = LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_8G
|
||||
|
||||
LSM6DSGyroRange = lsm6ds_ns.enum("LSM6DSGyroRange")
|
||||
LSM6DS_GYRO_RANGE_250DPS = LSM6DSGyroRange.LSM6DS_GYRO_RANGE_250DPS
|
||||
LSM6DS_GYRO_RANGE_500DPS = LSM6DSGyroRange.LSM6DS_GYRO_RANGE_500DPS
|
||||
LSM6DS_GYRO_RANGE_1000DPS = LSM6DSGyroRange.LSM6DS_GYRO_RANGE_1000DPS
|
||||
LSM6DS_GYRO_RANGE_2000DPS = LSM6DSGyroRange.LSM6DS_GYRO_RANGE_2000DPS
|
||||
LSM6DS_GYRO_RANGE_125DPS = LSM6DSGyroRange.LSM6DS_GYRO_RANGE_125DPS
|
||||
|
||||
LSM6DSODR = lsm6ds_ns.enum("LSM6DSODR")
|
||||
# Accelerometer ODR Values (matches ODR_XL[3:0] in CTRL1_XL)
|
||||
LSM6DS_ACCEL_ODR_OFF = LSM6DSODR.LSM6DS_ODR_OFF
|
||||
LSM6DS_ACCEL_ODR_12_5HZ = LSM6DSODR.LSM6DS_ODR_12_5HZ
|
||||
LSM6DS_ACCEL_ODR_26HZ = LSM6DSODR.LSM6DS_ODR_26HZ
|
||||
LSM6DS_ACCEL_ODR_52HZ = LSM6DSODR.LSM6DS_ODR_52HZ
|
||||
LSM6DS_ACCEL_ODR_104HZ = LSM6DSODR.LSM6DS_ODR_104HZ
|
||||
LSM6DS_ACCEL_ODR_208HZ = LSM6DSODR.LSM6DS_ODR_208HZ
|
||||
LSM6DS_ACCEL_ODR_416HZ = LSM6DSODR.LSM6DS_ODR_416HZ
|
||||
LSM6DS_ACCEL_ODR_833HZ = LSM6DSODR.LSM6DS_ODR_833HZ
|
||||
LSM6DS_ACCEL_ODR_1_66KHZ = LSM6DSODR.LSM6DS_ODR_1_66KHZ
|
||||
LSM6DS_ACCEL_ODR_3_33KHZ = LSM6DSODR.LSM6DS_ODR_3_33KHZ
|
||||
LSM6DS_ACCEL_ODR_6_66KHZ = LSM6DSODR.LSM6DS_ODR_6_66KHZ
|
||||
|
||||
# Gyroscope ODR Values (matches ODR_G[3:0] in CTRL2_G)
|
||||
LSM6DS_GYRO_ODR_OFF = LSM6DSODR.LSM6DS_ODR_OFF
|
||||
LSM6DS_GYRO_ODR_12_5HZ = LSM6DSODR.LSM6DS_ODR_12_5HZ
|
||||
LSM6DS_GYRO_ODR_26HZ = LSM6DSODR.LSM6DS_ODR_26HZ
|
||||
LSM6DS_GYRO_ODR_52HZ = LSM6DSODR.LSM6DS_ODR_52HZ
|
||||
LSM6DS_GYRO_ODR_104HZ = LSM6DSODR.LSM6DS_ODR_104HZ
|
||||
LSM6DS_GYRO_ODR_208HZ = LSM6DSODR.LSM6DS_ODR_208HZ
|
||||
LSM6DS_GYRO_ODR_416HZ = LSM6DSODR.LSM6DS_ODR_416HZ
|
||||
LSM6DS_GYRO_ODR_833HZ = LSM6DSODR.LSM6DS_ODR_833HZ
|
||||
LSM6DS_GYRO_ODR_1_66KHZ = LSM6DSODR.LSM6DS_ODR_1_66KHZ
|
||||
LSM6DS_GYRO_ODR_3_33KHZ = LSM6DSODR.LSM6DS_ODR_3_33KHZ
|
||||
LSM6DS_GYRO_ODR_6_66KHZ = LSM6DSODR.LSM6DS_ODR_6_66KHZ
|
||||
|
||||
CONF_ACCEL_X = "accel_x"
|
||||
CONF_ACCEL_Y = "accel_y"
|
||||
CONF_ACCEL_Z = "accel_z"
|
||||
CONF_GYRO_X = "gyro_x"
|
||||
CONF_GYRO_Y = "gyro_y"
|
||||
CONF_GYRO_Z = "gyro_z"
|
||||
CONF_ACCEL_RANGE = "accel_range"
|
||||
CONF_GYRO_RANGE = "gyro_range"
|
||||
CONF_ACCEL_ODR = "accel_odr"
|
||||
CONF_GYRO_ODR = "gyro_odr"
|
||||
@@ -1,74 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_METER_PER_SECOND_SQUARED,
|
||||
UNIT_DEGREE_PER_SECOND,
|
||||
UNIT_CELSIUS,
|
||||
ICON_ACCELERATION,
|
||||
ICON_THERMOMETER,
|
||||
ICON_SCREEN_ROTATION,
|
||||
CONF_ID,
|
||||
)
|
||||
from .const import (
|
||||
CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z,
|
||||
CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z,
|
||||
LSM6DSComponent,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(CONF_ID): cv.use_id(LSM6DSComponent),
|
||||
cv.Optional(CONF_ACCEL_X): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_ACCELERATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ACCEL_Y): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_ACCELERATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ACCEL_Z): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_ACCELERATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_GYRO_X): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_GYRO_Y): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_GYRO_Z): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("temperature"): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
})
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_ID])
|
||||
|
||||
for key in [CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z,
|
||||
CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z,
|
||||
"temperature"]:
|
||||
if key in config:
|
||||
sens = await sensor.new_sensor(config[key])
|
||||
cg.add(getattr(hub, f"set_{key}_sensor")(sens))
|
||||
@@ -0,0 +1,128 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_BRIEFCASE_DOWNLOAD,
|
||||
ICON_SCREEN_ROTATION,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_DEGREE_PER_SECOND,
|
||||
UNIT_METER_PER_SECOND_SQUARED,
|
||||
)
|
||||
|
||||
from .const import (
|
||||
CONF_ACCEL_ODR,
|
||||
CONF_ACCEL_RANGE,
|
||||
CONF_ACCEL_X,
|
||||
CONF_ACCEL_Y,
|
||||
CONF_ACCEL_Z,
|
||||
CONF_GYRO_ODR,
|
||||
CONF_GYRO_RANGE,
|
||||
CONF_GYRO_X,
|
||||
CONF_GYRO_Y,
|
||||
CONF_GYRO_Z,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@NonaSuomy"]
|
||||
|
||||
# Define the C++ namespace
|
||||
lsm6ds_ns = cg.esphome_ns.namespace("lsm6ds_base")
|
||||
LSM6DSComponent = lsm6ds_ns.class_("LSM6DSComponent", cg.PollingComponent)
|
||||
|
||||
LSM6DSAccelRange = lsm6ds_ns.enum("LSM6DSAccelRange")
|
||||
ACCEL_RANGE_OPTIONS = {
|
||||
"2G": LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_2G,
|
||||
"4G": LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_4G,
|
||||
"8G": LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_8G,
|
||||
"16G": LSM6DSAccelRange.LSM6DS_ACCEL_RANGE_16G,
|
||||
}
|
||||
|
||||
LSM6DSGyroRange = lsm6ds_ns.enum("LSM6DSGyroRange")
|
||||
GYRO_RANGE_OPTIONS = {
|
||||
"125DPS": LSM6DSGyroRange.LSM6DS_GYRO_RANGE_125DPS,
|
||||
"250DPS": LSM6DSGyroRange.LSM6DS_GYRO_RANGE_250DPS,
|
||||
"500DPS": LSM6DSGyroRange.LSM6DS_GYRO_RANGE_500DPS,
|
||||
"1000DPS": LSM6DSGyroRange.LSM6DS_GYRO_RANGE_1000DPS,
|
||||
"2000DPS": LSM6DSGyroRange.LSM6DS_GYRO_RANGE_2000DPS,
|
||||
}
|
||||
|
||||
LSM6DSODR = lsm6ds_ns.enum("LSM6DSODR")
|
||||
ODR_OPTIONS = {
|
||||
"12.5HZ": LSM6DSODR.LSM6DS_ODR_12_5HZ,
|
||||
"26HZ": LSM6DSODR.LSM6DS_ODR_26HZ,
|
||||
"52HZ": LSM6DSODR.LSM6DS_ODR_52HZ,
|
||||
"104HZ": LSM6DSODR.LSM6DS_ODR_104HZ,
|
||||
"208HZ": LSM6DSODR.LSM6DS_ODR_208HZ,
|
||||
"416HZ": LSM6DSODR.LSM6DS_ODR_416HZ,
|
||||
"833HZ": LSM6DSODR.LSM6DS_ODR_833HZ,
|
||||
"1660HZ": LSM6DSODR.LSM6DS_ODR_1660HZ,
|
||||
"3330HZ": LSM6DSODR.LSM6DS_ODR_3330HZ,
|
||||
"6660HZ": LSM6DSODR.LSM6DS_ODR_6660HZ,
|
||||
}
|
||||
|
||||
accel_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_BRIEFCASE_DOWNLOAD,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
gyro_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
temperature_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
LSM6DS_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LSM6DSComponent),
|
||||
cv.Optional(CONF_ACCEL_X): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Y): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Z): accel_schema,
|
||||
cv.Optional(CONF_GYRO_X): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Y): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Z): gyro_schema,
|
||||
cv.Optional(CONF_TEMPERATURE): temperature_schema,
|
||||
cv.Optional(CONF_ACCEL_RANGE, default="2G"): cv.enum(
|
||||
ACCEL_RANGE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_GYRO_RANGE, default="250DPS"): cv.enum(
|
||||
GYRO_RANGE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_ACCEL_ODR, default="104HZ"): cv.enum(ODR_OPTIONS, upper=True),
|
||||
cv.Optional(CONF_GYRO_ODR, default="104HZ"): cv.enum(ODR_OPTIONS, upper=True),
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
|
||||
|
||||
async def register_lsm6ds(var, config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# Set configuration options
|
||||
cg.add(var.set_accel_range(config[CONF_ACCEL_RANGE]))
|
||||
cg.add(var.set_gyro_range(config[CONF_GYRO_RANGE]))
|
||||
cg.add(var.set_accel_odr(config[CONF_ACCEL_ODR]))
|
||||
cg.add(var.set_gyro_odr(config[CONF_GYRO_ODR]))
|
||||
|
||||
# Set up sensor outputs
|
||||
for d in ["x", "y", "z"]:
|
||||
accel_key = f"accel_{d}"
|
||||
if accel_key in config:
|
||||
sens = await sensor.new_sensor(config[accel_key])
|
||||
cg.add(getattr(var, f"set_accel_{d}_sensor")(sens))
|
||||
gyro_key = f"gyro_{d}"
|
||||
if gyro_key in config:
|
||||
sens = await sensor.new_sensor(config[gyro_key])
|
||||
cg.add(getattr(var, f"set_gyro_{d}_sensor")(sens))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
@@ -0,0 +1,26 @@
|
||||
# Registers
|
||||
LSM6DS_WHO_AM_I = 0x0F
|
||||
LSM6DS_CTRL1_XL = 0x10
|
||||
LSM6DS_CTRL2_G = 0x11
|
||||
LSM6DS_CTRL3_C = 0x12
|
||||
LSM6DS_STATUS_REG = 0x1E
|
||||
LSM6DS_OUT_TEMP_L = 0x20
|
||||
LSM6DS_OUTX_L_G = 0x22
|
||||
LSM6DS_OUTX_L_A = 0x28
|
||||
|
||||
# WHO_AM_I Values for different variants
|
||||
LSM6DSOX_WHO_AM_I_VALUE = 0x6C
|
||||
LSM6DS3TR_WHO_AM_I_VALUE = 0x6A
|
||||
LSM6DSO32TR_WHO_AM_I_VALUE = 0x69
|
||||
|
||||
CONF_LSM6DS_ID = "lsm6ds_id"
|
||||
CONF_ACCEL_X = "accel_x"
|
||||
CONF_ACCEL_Y = "accel_y"
|
||||
CONF_ACCEL_Z = "accel_z"
|
||||
CONF_GYRO_X = "gyro_x"
|
||||
CONF_GYRO_Y = "gyro_y"
|
||||
CONF_GYRO_Z = "gyro_z"
|
||||
CONF_ACCEL_RANGE = "accel_range"
|
||||
CONF_GYRO_RANGE = "gyro_range"
|
||||
CONF_ACCEL_ODR = "accel_odr"
|
||||
CONF_GYRO_ODR = "gyro_odr"
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lsm6ds {
|
||||
namespace lsm6ds_base {
|
||||
|
||||
static const char *const TAG = "lsm6ds";
|
||||
|
||||
@@ -28,14 +28,14 @@ void LSM6DSComponent::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
// SW Reset
|
||||
// SW Reset
|
||||
this->write_register(0x12, 0x01);
|
||||
uint8_t ctrl3;
|
||||
|
||||
|
||||
// Use esphome:: namespace for framework compatibility
|
||||
uint32_t start = esphome::millis();
|
||||
uint32_t start = esphome::millis();
|
||||
do {
|
||||
esphome::delay(10);
|
||||
esphome::delay(10);
|
||||
this->read_register(0x12, &ctrl3, 1);
|
||||
} while ((ctrl3 & 0x01) && (esphome::millis() - start < 100));
|
||||
|
||||
@@ -44,33 +44,53 @@ void LSM6DSComponent::setup() {
|
||||
}
|
||||
|
||||
// Enable BDU (Block Data Update)
|
||||
this->write_register(0x12, 0x40 | 0x04); // BDU=1, IF_INC=1
|
||||
this->write_register(0x12, 0x40 | 0x04); // BDU=1, IF_INC=1
|
||||
|
||||
// Configure Accel
|
||||
uint8_t ctrl1_xl = (this->accel_odr_ << 4) | (this->accel_range_ << 2);
|
||||
this->write_register(0x10, ctrl1_xl);
|
||||
|
||||
|
||||
switch (this->accel_range_) {
|
||||
case LSM6DS_ACCEL_RANGE_2G: accel_sensitivity_ = 0.061e-3f; break;
|
||||
case LSM6DS_ACCEL_RANGE_4G: accel_sensitivity_ = 0.122e-3f; break;
|
||||
case LSM6DS_ACCEL_RANGE_8G: accel_sensitivity_ = 0.244e-3f; break;
|
||||
case LSM6DS_ACCEL_RANGE_16G: accel_sensitivity_ = 0.488e-3f; break;
|
||||
default: accel_sensitivity_ = 0; break;
|
||||
case LSM6DS_ACCEL_RANGE_2G:
|
||||
this->accel_sensitivity_ = 0.061e-3f;
|
||||
break;
|
||||
case LSM6DS_ACCEL_RANGE_4G:
|
||||
this->accel_sensitivity_ = 0.122e-3f;
|
||||
break;
|
||||
case LSM6DS_ACCEL_RANGE_8G:
|
||||
this->accel_sensitivity_ = 0.244e-3f;
|
||||
break;
|
||||
case LSM6DS_ACCEL_RANGE_16G:
|
||||
this->accel_sensitivity_ = 0.488e-3f;
|
||||
break;
|
||||
default:
|
||||
this->accel_sensitivity_ = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Configure Gyro
|
||||
uint8_t ctrl2_g = (this->gyro_odr_ << 4);
|
||||
if (this->gyro_range_ == LSM6DS_GYRO_RANGE_125DPS) {
|
||||
ctrl2_g |= 0x02; // FS_125 = 1
|
||||
gyro_sensitivity_ = 4.375e-3f;
|
||||
ctrl2_g |= 0x02; // FS_125 = 1
|
||||
this->gyro_sensitivity_ = 4.375e-3f;
|
||||
} else {
|
||||
ctrl2_g |= (this->gyro_range_ << 2);
|
||||
switch (this->gyro_range_) {
|
||||
case LSM6DS_GYRO_RANGE_250DPS: gyro_sensitivity_ = 8.75e-3f; break;
|
||||
case LSM6DS_GYRO_RANGE_500DPS: gyro_sensitivity_ = 17.50e-3f; break;
|
||||
case LSM6DS_GYRO_RANGE_1000DPS: gyro_sensitivity_ = 35.0e-3f; break;
|
||||
case LSM6DS_GYRO_RANGE_2000DPS: gyro_sensitivity_ = 70.0e-3f; break;
|
||||
default: gyro_sensitivity_ = 0; break;
|
||||
case LSM6DS_GYRO_RANGE_250DPS:
|
||||
this->gyro_sensitivity_ = 8.75e-3f;
|
||||
break;
|
||||
case LSM6DS_GYRO_RANGE_500DPS:
|
||||
this->gyro_sensitivity_ = 17.50e-3f;
|
||||
break;
|
||||
case LSM6DS_GYRO_RANGE_1000DPS:
|
||||
this->gyro_sensitivity_ = 35.0e-3f;
|
||||
break;
|
||||
case LSM6DS_GYRO_RANGE_2000DPS:
|
||||
this->gyro_sensitivity_ = 70.0e-3f;
|
||||
break;
|
||||
default:
|
||||
this->gyro_sensitivity_ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->write_register(0x11, ctrl2_g);
|
||||
@@ -78,38 +98,46 @@ void LSM6DSComponent::setup() {
|
||||
|
||||
void LSM6DSComponent::update() {
|
||||
uint8_t status;
|
||||
if (!this->read_register(0x1E, &status, 1)) return;
|
||||
if (!this->read_register(0x1E, &status, 1))
|
||||
return;
|
||||
|
||||
if (status & 0x01) { // GDA - Gyroscope data available
|
||||
if (status & 0x01) { // GDA - Gyroscope data available
|
||||
uint8_t data[6];
|
||||
if (this->read_register(0x22, data, 6)) {
|
||||
int16_t gx = encode_uint16(data[1], data[0]);
|
||||
int16_t gy = encode_uint16(data[3], data[2]);
|
||||
int16_t gz = encode_uint16(data[5], data[4]);
|
||||
if (this->gyro_x_sensor_) this->gyro_x_sensor_->publish_state(gx * gyro_sensitivity_);
|
||||
if (this->gyro_y_sensor_) this->gyro_y_sensor_->publish_state(gy * gyro_sensitivity_);
|
||||
if (this->gyro_z_sensor_) this->gyro_z_sensor_->publish_state(gz * gyro_sensitivity_);
|
||||
if (this->gyro_x_sensor_)
|
||||
this->gyro_x_sensor_->publish_state(gx * this->gyro_sensitivity_);
|
||||
if (this->gyro_y_sensor_)
|
||||
this->gyro_y_sensor_->publish_state(gy * this->gyro_sensitivity_);
|
||||
if (this->gyro_z_sensor_)
|
||||
this->gyro_z_sensor_->publish_state(gz * this->gyro_sensitivity_);
|
||||
}
|
||||
}
|
||||
|
||||
if (status & 0x02) { // XLDA - Accelerometer data available
|
||||
if (status & 0x02) { // XLDA - Accelerometer data available
|
||||
uint8_t data[6];
|
||||
if (this->read_register(0x28, data, 6)) {
|
||||
int16_t ax = encode_uint16(data[1], data[0]);
|
||||
int16_t ay = encode_uint16(data[3], data[2]);
|
||||
int16_t az = encode_uint16(data[5], data[4]);
|
||||
float grav = 9.80665f;
|
||||
if (this->accel_x_sensor_) this->accel_x_sensor_->publish_state(ax * accel_sensitivity_ * grav);
|
||||
if (this->accel_y_sensor_) this->accel_y_sensor_->publish_state(ay * accel_sensitivity_ * grav);
|
||||
if (this->accel_z_sensor_) this->accel_z_sensor_->publish_state(az * accel_sensitivity_ * grav);
|
||||
if (this->accel_x_sensor_)
|
||||
this->accel_x_sensor_->publish_state(ax * this->accel_sensitivity_ * grav);
|
||||
if (this->accel_y_sensor_)
|
||||
this->accel_y_sensor_->publish_state(ay * this->accel_sensitivity_ * grav);
|
||||
if (this->accel_z_sensor_)
|
||||
this->accel_z_sensor_->publish_state(az * this->accel_sensitivity_ * grav);
|
||||
}
|
||||
}
|
||||
|
||||
if (status & 0x04) { // TDA - Temperature data available
|
||||
if (status & 0x04) { // TDA - Temperature data available
|
||||
uint8_t data[2];
|
||||
if (this->read_register(0x20, data, 2)) {
|
||||
int16_t temp = encode_uint16(data[1], data[0]);
|
||||
if (this->temperature_sensor_) this->temperature_sensor_->publish_state(25.0f + (temp / 256.0f));
|
||||
if (this->temperature_sensor_)
|
||||
this->temperature_sensor_->publish_state(25.0f + (temp / 256.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,5 +158,5 @@ void LSM6DSComponent::dump_config() {
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
}
|
||||
|
||||
} // namespace lsm6ds
|
||||
} // namespace lsm6ds_base
|
||||
} // namespace esphome
|
||||
@@ -2,14 +2,9 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
#ifdef USE_SPI
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace lsm6ds {
|
||||
namespace lsm6ds_base {
|
||||
|
||||
enum LSM6DSAccelRange {
|
||||
LSM6DS_ACCEL_RANGE_2G = 0x00,
|
||||
@@ -81,35 +76,5 @@ class LSM6DSComponent : public PollingComponent {
|
||||
float gyro_sensitivity_;
|
||||
};
|
||||
|
||||
class LSM6DSI2CDevice : public LSM6DSComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
bool read_register(uint8_t reg, uint8_t *data, size_t len) override {
|
||||
return this->read_bytes(reg, data, len);
|
||||
}
|
||||
bool write_register(uint8_t reg, uint8_t data) override {
|
||||
return this->write_byte(reg, data);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef USE_SPI
|
||||
class LSM6DSSPIDevice : public LSM6DSComponent, public spi::SPIDevice {
|
||||
public:
|
||||
bool read_register(uint8_t reg, uint8_t *data, size_t len) override {
|
||||
this->enable();
|
||||
this->transfer_byte(reg | 0x80);
|
||||
this->transfer_bytes(nullptr, data, len);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
bool write_register(uint8_t reg, uint8_t data) override {
|
||||
this->enable();
|
||||
this->transfer_byte(reg & 0x7F);
|
||||
this->transfer_byte(data);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace lsm6ds
|
||||
} // namespace lsm6ds_base
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,124 @@
|
||||
# LSM6DS I2C Example Configuration
|
||||
|
||||
This component supports LSM6DS family IMU sensors (accelerometer + gyroscope) via I2C interface.
|
||||
|
||||
## Supported Sensors
|
||||
- LSM6DSOX (WHO_AM_I: 0x6C, I2C address: 0x6A or 0x6B)
|
||||
- LSM6DS3TR-C (WHO_AM_I: 0x6A, I2C address: 0x6A or 0x6B)
|
||||
- LSM6DSO32 (WHO_AM_I: 0x69, I2C address: 0x6A or 0x6B)
|
||||
|
||||
## Example 1: All-in-one Pattern
|
||||
|
||||
Configure the sensor with all outputs in the component block:
|
||||
|
||||
```yaml
|
||||
i2c:
|
||||
- id: i2c_bus
|
||||
sda: GPIO21
|
||||
scl: GPIO22
|
||||
scan: true
|
||||
|
||||
lsm6ds_i2c:
|
||||
id: imu_sensor
|
||||
i2c_id: i2c_bus
|
||||
address: 0x6B
|
||||
accel_range: 4G
|
||||
gyro_range: 500DPS
|
||||
accel_odr: 104HZ
|
||||
gyro_odr: 104HZ
|
||||
update_interval: 50ms
|
||||
accel_x:
|
||||
name: "Accel X"
|
||||
accel_y:
|
||||
name: "Accel Y"
|
||||
accel_z:
|
||||
name: "Accel Z"
|
||||
gyro_x:
|
||||
name: "Gyro X"
|
||||
gyro_y:
|
||||
name: "Gyro Y"
|
||||
gyro_z:
|
||||
name: "Gyro Z"
|
||||
temperature:
|
||||
name: "IMU Temperature"
|
||||
```
|
||||
|
||||
## Example 2: Hub + Sensor Platform Pattern
|
||||
|
||||
Configure the hub separately, then add sensor outputs under the sensor platform:
|
||||
|
||||
```yaml
|
||||
i2c:
|
||||
- id: i2c_bus
|
||||
sda: GPIO21
|
||||
scl: GPIO22
|
||||
scan: true
|
||||
|
||||
lsm6ds_i2c:
|
||||
id: imu_hub
|
||||
i2c_id: i2c_bus
|
||||
address: 0x6B
|
||||
accel_range: 4G
|
||||
gyro_range: 500DPS
|
||||
accel_odr: 104HZ
|
||||
gyro_odr: 104HZ
|
||||
update_interval: 50ms
|
||||
|
||||
sensor:
|
||||
- platform: lsm6ds_i2c
|
||||
lsm6ds_id: imu_hub
|
||||
accel_x:
|
||||
name: "Accel X"
|
||||
accel_y:
|
||||
name: "Accel Y"
|
||||
accel_z:
|
||||
name: "Accel Z"
|
||||
gyro_x:
|
||||
name: "Gyro X"
|
||||
gyro_y:
|
||||
name: "Gyro Y"
|
||||
gyro_z:
|
||||
name: "Gyro Z"
|
||||
temperature:
|
||||
name: "IMU Temperature"
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### I2C Address
|
||||
- `0x6A` (default, SDO/SA0 pin LOW)
|
||||
- `0x6B` (SDO/SA0 pin HIGH)
|
||||
|
||||
### Accelerometer Range
|
||||
- `2G` (default)
|
||||
- `4G`
|
||||
- `8G`
|
||||
- `16G`
|
||||
|
||||
### Gyroscope Range
|
||||
- `125DPS`
|
||||
- `250DPS` (default)
|
||||
- `500DPS`
|
||||
- `1000DPS`
|
||||
- `2000DPS`
|
||||
|
||||
### Output Data Rate (ODR)
|
||||
- `12.5HZ`
|
||||
- `26HZ`
|
||||
- `52HZ`
|
||||
- `104HZ` (default)
|
||||
- `208HZ`
|
||||
- `416HZ`
|
||||
- `833HZ`
|
||||
- `1660HZ`
|
||||
- `3330HZ`
|
||||
- `6660HZ`
|
||||
|
||||
## I2C Configuration
|
||||
|
||||
The LSM6DS I2C implementation uses standard I2C communication:
|
||||
- **Speed**: Supports up to 400 kHz (Fast mode)
|
||||
- **7-bit addressing**: 0x6A or 0x6B depending on SDO/SA0 pin state
|
||||
- **Auto-increment**: Multi-byte reads automatically increment register address
|
||||
|
||||
The component handles the I2C protocol automatically.
|
||||
@@ -0,0 +1,27 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
from ..lsm6ds_base import LSM6DS_SCHEMA, LSM6DSComponent, register_lsm6ds
|
||||
|
||||
AUTO_LOAD = ["lsm6ds_base"]
|
||||
CODEOWNERS = ["@NonaSuomy"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
lsm6ds_i2c_ns = cg.esphome_ns.namespace("lsm6ds_i2c")
|
||||
LSM6DSI2CDevice = lsm6ds_i2c_ns.class_(
|
||||
"LSM6DSI2CDevice", LSM6DSComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = LSM6DS_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LSM6DSI2CDevice),
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x6A))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await register_lsm6ds(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "lsm6ds_i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lsm6ds_i2c {} // namespace lsm6ds_i2c
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/lsm6ds_base/lsm6ds.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lsm6ds_i2c {
|
||||
|
||||
class LSM6DSI2CDevice : public lsm6ds_base::LSM6DSComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
bool read_register(uint8_t reg, uint8_t *data, size_t len) override { return this->read_bytes(reg, data, len); }
|
||||
bool write_register(uint8_t reg, uint8_t data) override { return this->write_byte(reg, data); }
|
||||
};
|
||||
|
||||
} // namespace lsm6ds_i2c
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,77 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
ICON_BRIEFCASE_DOWNLOAD,
|
||||
ICON_SCREEN_ROTATION,
|
||||
ICON_THERMOMETER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_DEGREE_PER_SECOND,
|
||||
UNIT_METER_PER_SECOND_SQUARED,
|
||||
)
|
||||
|
||||
from ..lsm6ds_base import LSM6DSComponent
|
||||
from ..lsm6ds_base.const import (
|
||||
CONF_ACCEL_X,
|
||||
CONF_ACCEL_Y,
|
||||
CONF_ACCEL_Z,
|
||||
CONF_GYRO_X,
|
||||
CONF_GYRO_Y,
|
||||
CONF_GYRO_Z,
|
||||
CONF_LSM6DS_ID,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["lsm6ds_i2c"]
|
||||
CODEOWNERS = ["@NonaSuomy"]
|
||||
|
||||
accel_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_BRIEFCASE_DOWNLOAD,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
gyro_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
temperature_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LSM6DS_ID): cv.use_id(LSM6DSComponent),
|
||||
cv.Optional(CONF_ACCEL_X): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Y): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Z): accel_schema,
|
||||
cv.Optional(CONF_GYRO_X): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Y): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Z): gyro_schema,
|
||||
cv.Optional(CONF_TEMPERATURE): temperature_schema,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_LSM6DS_ID])
|
||||
|
||||
for d in ["x", "y", "z"]:
|
||||
accel_key = f"accel_{d}"
|
||||
if accel_key in config:
|
||||
sens = await sensor.new_sensor(config[accel_key])
|
||||
cg.add(getattr(hub, f"set_accel_{d}_sensor")(sens))
|
||||
gyro_key = f"gyro_{d}"
|
||||
if gyro_key in config:
|
||||
sens = await sensor.new_sensor(config[gyro_key])
|
||||
cg.add(getattr(hub, f"set_gyro_{d}_sensor")(sens))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(hub.set_temperature_sensor(sens))
|
||||
@@ -0,0 +1,122 @@
|
||||
# LSM6DS SPI Example Configuration
|
||||
|
||||
This component supports LSM6DS family IMU sensors (accelerometer + gyroscope) via SPI interface.
|
||||
|
||||
## Supported Sensors
|
||||
- LSM6DSOX (WHO_AM_I: 0x6C)
|
||||
- LSM6DS3TR-C (WHO_AM_I: 0x6A)
|
||||
- LSM6DSO32 (WHO_AM_I: 0x69)
|
||||
|
||||
## Example 1: All-in-one Pattern
|
||||
|
||||
Configure the sensor with all outputs in the component block:
|
||||
|
||||
```yaml
|
||||
spi:
|
||||
- id: spi_bus
|
||||
clk_pin: GPIO12
|
||||
mosi_pin: GPIO11
|
||||
miso_pin: GPIO13
|
||||
|
||||
lsm6ds_spi:
|
||||
id: imu_sensor
|
||||
spi_id: spi_bus
|
||||
cs_pin: GPIO10
|
||||
accel_range: 4G
|
||||
gyro_range: 500DPS
|
||||
accel_odr: 104HZ
|
||||
gyro_odr: 104HZ
|
||||
update_interval: 50ms
|
||||
accel_x:
|
||||
name: "Accel X"
|
||||
accel_y:
|
||||
name: "Accel Y"
|
||||
accel_z:
|
||||
name: "Accel Z"
|
||||
gyro_x:
|
||||
name: "Gyro X"
|
||||
gyro_y:
|
||||
name: "Gyro Y"
|
||||
gyro_z:
|
||||
name: "Gyro Z"
|
||||
temperature:
|
||||
name: "IMU Temperature"
|
||||
```
|
||||
|
||||
## Example 2: Hub + Sensor Platform Pattern
|
||||
|
||||
Configure the hub separately, then add sensor outputs under the sensor platform:
|
||||
|
||||
```yaml
|
||||
spi:
|
||||
- id: spi_bus
|
||||
clk_pin: GPIO12
|
||||
mosi_pin: GPIO11
|
||||
miso_pin: GPIO13
|
||||
|
||||
lsm6ds_spi:
|
||||
id: imu_hub
|
||||
spi_id: spi_bus
|
||||
cs_pin: GPIO10
|
||||
accel_range: 4G
|
||||
gyro_range: 500DPS
|
||||
accel_odr: 104HZ
|
||||
gyro_odr: 104HZ
|
||||
update_interval: 50ms
|
||||
|
||||
sensor:
|
||||
- platform: lsm6ds_spi
|
||||
lsm6ds_id: imu_hub
|
||||
accel_x:
|
||||
name: "Accel X"
|
||||
accel_y:
|
||||
name: "Accel Y"
|
||||
accel_z:
|
||||
name: "Accel Z"
|
||||
gyro_x:
|
||||
name: "Gyro X"
|
||||
gyro_y:
|
||||
name: "Gyro Y"
|
||||
gyro_z:
|
||||
name: "Gyro Z"
|
||||
temperature:
|
||||
name: "IMU Temperature"
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Accelerometer Range
|
||||
- `2G` (default)
|
||||
- `4G`
|
||||
- `8G`
|
||||
- `16G`
|
||||
|
||||
### Gyroscope Range
|
||||
- `125DPS`
|
||||
- `250DPS` (default)
|
||||
- `500DPS`
|
||||
- `1000DPS`
|
||||
- `2000DPS`
|
||||
|
||||
### Output Data Rate (ODR)
|
||||
- `12.5HZ`
|
||||
- `26HZ`
|
||||
- `52HZ`
|
||||
- `104HZ` (default)
|
||||
- `208HZ`
|
||||
- `416HZ`
|
||||
- `833HZ`
|
||||
- `1660HZ`
|
||||
- `3330HZ`
|
||||
- `6660HZ`
|
||||
|
||||
## SPI Configuration
|
||||
|
||||
The LSM6DS SPI implementation uses:
|
||||
- **Mode 0** or **Mode 3** (CPOL=0, CPHA=0 or CPOL=1, CPHA=1)
|
||||
- **MSB first**
|
||||
- **Read bit**: MSB set to 1 (reg | 0x80)
|
||||
- **Write bit**: MSB set to 0 (reg & 0x7F)
|
||||
- **Max speed**: 10 MHz
|
||||
|
||||
The component handles the SPI protocol automatically.
|
||||
@@ -0,0 +1,31 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import spi
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
from ..lsm6ds_base import LSM6DS_SCHEMA, LSM6DSComponent, register_lsm6ds
|
||||
|
||||
AUTO_LOAD = ["lsm6ds_base"]
|
||||
CODEOWNERS = ["@NonaSuomy"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
||||
lsm6ds_spi_ns = cg.esphome_ns.namespace("lsm6ds_spi")
|
||||
LSM6DSSPIDevice = lsm6ds_spi_ns.class_(
|
||||
"LSM6DSSPIDevice", LSM6DSComponent, spi.SPIDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LSM6DSSPIDevice),
|
||||
}
|
||||
)
|
||||
.extend(LSM6DS_SCHEMA)
|
||||
.extend(spi.spi_device_schema(cs_pin_required=True))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await register_lsm6ds(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "lsm6ds_spi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lsm6ds_spi {} // namespace lsm6ds_spi
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/lsm6ds_base/lsm6ds.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lsm6ds_spi {
|
||||
|
||||
class LSM6DSSPIDevice : public lsm6ds_base::LSM6DSComponent,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
||||
public:
|
||||
bool read_register(uint8_t reg, uint8_t *data, size_t len) override {
|
||||
this->enable();
|
||||
this->transfer_byte(reg | 0x80);
|
||||
this->transfer_bytes(nullptr, data, len);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
bool write_register(uint8_t reg, uint8_t data) override {
|
||||
this->enable();
|
||||
this->transfer_byte(reg & 0x7F);
|
||||
this->transfer_byte(data);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace lsm6ds_spi
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,77 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
ICON_BRIEFCASE_DOWNLOAD,
|
||||
ICON_SCREEN_ROTATION,
|
||||
ICON_THERMOMETER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_DEGREE_PER_SECOND,
|
||||
UNIT_METER_PER_SECOND_SQUARED,
|
||||
)
|
||||
|
||||
from ..lsm6ds_base import LSM6DSComponent
|
||||
from ..lsm6ds_base.const import (
|
||||
CONF_ACCEL_X,
|
||||
CONF_ACCEL_Y,
|
||||
CONF_ACCEL_Z,
|
||||
CONF_GYRO_X,
|
||||
CONF_GYRO_Y,
|
||||
CONF_GYRO_Z,
|
||||
CONF_LSM6DS_ID,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["lsm6ds_spi"]
|
||||
CODEOWNERS = ["@NonaSuomy"]
|
||||
|
||||
accel_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_BRIEFCASE_DOWNLOAD,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
gyro_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
temperature_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LSM6DS_ID): cv.use_id(LSM6DSComponent),
|
||||
cv.Optional(CONF_ACCEL_X): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Y): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Z): accel_schema,
|
||||
cv.Optional(CONF_GYRO_X): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Y): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Z): gyro_schema,
|
||||
cv.Optional(CONF_TEMPERATURE): temperature_schema,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_LSM6DS_ID])
|
||||
|
||||
for d in ["x", "y", "z"]:
|
||||
accel_key = f"accel_{d}"
|
||||
if accel_key in config:
|
||||
sens = await sensor.new_sensor(config[accel_key])
|
||||
cg.add(getattr(hub, f"set_accel_{d}_sensor")(sens))
|
||||
gyro_key = f"gyro_{d}"
|
||||
if gyro_key in config:
|
||||
sens = await sensor.new_sensor(config[gyro_key])
|
||||
cg.add(getattr(hub, f"set_gyro_{d}_sensor")(sens))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(hub.set_temperature_sensor(sens))
|
||||
Reference in new issue
Block a user