mirror of
https://github.com/jdillenburg/esphome.git
synced 2026-03-18 12:19:14 -06:00
Added ADXL345 external component
This commit is contained in:
@@ -6,6 +6,7 @@ Welcome to my ESPhome configuration directory. This repository contains the ESPh
|
|||||||
| Component | Description |
|
| Component | Description |
|
||||||
|-----------|-------------|
|
|-----------|-------------|
|
||||||
| [TFmini](components/tfmini) | External component for the TFmini-S time of flight distance sensor with distance, signal strength, and temperature readings |
|
| [TFmini](components/tfmini) | External component for the TFmini-S time of flight distance sensor with distance, signal strength, and temperature readings |
|
||||||
|
| [Adxl345](components/adxl345) | External component for the ADXL345 accelerometer. Measures acceleration along x, y and z axes. |
|
||||||
|
|
||||||
## Devices
|
## Devices
|
||||||
|
|
||||||
|
|||||||
21
components/adxl345/LICENSE
Normal file
21
components/adxl345/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Your Name
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
129
components/adxl345/README.md
Normal file
129
components/adxl345/README.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# ADXL345 Component for ESPHome
|
||||||
|
|
||||||
|
This is a custom component for ESPHome that interfaces with the ADXL345 3-axis accelerometer.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Supports all standard I2C configuration options including multiple I2C buses
|
||||||
|
- Configurable measurement range (2G, 4G, 8G, 16G)
|
||||||
|
- Provides raw acceleration values for X, Y, and Z axes
|
||||||
|
- Calculates off-vertical angle (useful for bed/chair position tracking)
|
||||||
|
- Calculates jitter (total movement across all axes)
|
||||||
|
- Full Home Assistant integration with proper device classes and units
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Create a `components` directory in your ESPHome configuration directory if it doesn't exist
|
||||||
|
2. Create an `adxl345` directory inside the `components` directory
|
||||||
|
3. Copy the following files into the `adxl345` directory:
|
||||||
|
- `__init__.py`
|
||||||
|
- `adxl345.h`
|
||||||
|
- `adxl345.cpp`
|
||||||
|
- `sensor.py`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This component requires the following libraries:
|
||||||
|
- Wire
|
||||||
|
- SPI
|
||||||
|
- Adafruit BusIO
|
||||||
|
- Adafruit Unified Sensor
|
||||||
|
- Adafruit ADXL345
|
||||||
|
|
||||||
|
Add these to your ESPHome configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
libraries:
|
||||||
|
- "Wire"
|
||||||
|
- "SPI"
|
||||||
|
- "Adafruit BusIO"
|
||||||
|
- "Adafruit Unified Sensor"
|
||||||
|
- "Adafruit ADXL345"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Define component
|
||||||
|
adxl345:
|
||||||
|
- id: my_adxl345
|
||||||
|
address: 0x53
|
||||||
|
update_interval: 100ms
|
||||||
|
|
||||||
|
# Define sensors from component
|
||||||
|
sensor:
|
||||||
|
- platform: adxl345
|
||||||
|
id: my_adxl345
|
||||||
|
accel_x:
|
||||||
|
name: "Acceleration X"
|
||||||
|
accel_y:
|
||||||
|
name: "Acceleration Y"
|
||||||
|
accel_z:
|
||||||
|
name: "Acceleration Z"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Define I2C bus
|
||||||
|
i2c:
|
||||||
|
sda: GPIO21
|
||||||
|
scl: GPIO22
|
||||||
|
frequency: 400kHz
|
||||||
|
|
||||||
|
# Define component with all options
|
||||||
|
adxl345:
|
||||||
|
- id: my_adxl345
|
||||||
|
address: 0x53
|
||||||
|
update_interval: 100ms
|
||||||
|
range: 4G # Options: 2G, 4G, 8G, 16G
|
||||||
|
|
||||||
|
# Define sensors with filtering
|
||||||
|
sensor:
|
||||||
|
- platform: adxl345
|
||||||
|
id: my_adxl345
|
||||||
|
accel_x:
|
||||||
|
name: "Acceleration X"
|
||||||
|
filters:
|
||||||
|
- median:
|
||||||
|
window_size: 5
|
||||||
|
send_every: 5
|
||||||
|
send_first_at: 1
|
||||||
|
accel_y:
|
||||||
|
name: "Acceleration Y"
|
||||||
|
accel_z:
|
||||||
|
name: "Acceleration Z"
|
||||||
|
off_vertical:
|
||||||
|
name: "Tilt Angle"
|
||||||
|
filters:
|
||||||
|
- sliding_window_moving_average:
|
||||||
|
window_size: 10
|
||||||
|
send_every: 5
|
||||||
|
jitter:
|
||||||
|
name: "Movement Detection"
|
||||||
|
filters:
|
||||||
|
- threshold:
|
||||||
|
above: 10.0
|
||||||
|
then: ON
|
||||||
|
below: 2.0
|
||||||
|
then: OFF
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Sensors
|
||||||
|
|
||||||
|
| Sensor | Description | Unit |
|
||||||
|
|--------|-------------|------|
|
||||||
|
| `accel_x` | Raw X-axis acceleration | m/s² |
|
||||||
|
| `accel_y` | Raw Y-axis acceleration | m/s² |
|
||||||
|
| `accel_z` | Raw Z-axis acceleration | m/s² |
|
||||||
|
| `off_vertical` | Maximum angle from vertical (derived from pitch and roll) | degrees |
|
||||||
|
| `jitter` | Sum of absolute accelerations across all axes (movement detection) | m/s² |
|
||||||
|
|
||||||
|
## Example Use Cases
|
||||||
|
|
||||||
|
- Bed/chair incline monitoring
|
||||||
|
- Movement detection
|
||||||
|
- Vibration monitoring
|
||||||
|
- Orientation sensing
|
||||||
93
components/adxl345/__init__.py
Normal file
93
components/adxl345/__init__.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_ID, ICON_RULER, STATE_CLASS_MEASUREMENT
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
|
||||||
|
DEPENDENCIES = ['i2c']
|
||||||
|
AUTO_LOAD = ['sensor']
|
||||||
|
MULTI_CONF = True
|
||||||
|
CODEOWNERS = ["@jdillenburg"]
|
||||||
|
|
||||||
|
adxl345_ns = cg.esphome_ns.namespace('adxl345')
|
||||||
|
ADXL345Component = adxl345_ns.class_('ADXL345Component', cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
|
CONF_RANGE = "range"
|
||||||
|
RANGE_2G = 0
|
||||||
|
RANGE_4G = 1
|
||||||
|
RANGE_8G = 2
|
||||||
|
RANGE_16G = 3
|
||||||
|
|
||||||
|
CONF_OFF_VERTICAL = "off_vertical"
|
||||||
|
CONF_JITTER = "jitter"
|
||||||
|
CONF_ACCEL_X = "accel_x"
|
||||||
|
CONF_ACCEL_Y = "accel_y"
|
||||||
|
CONF_ACCEL_Z = "accel_z"
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(ADXL345Component),
|
||||||
|
cv.Optional(CONF_RANGE, default="2G"): cv.enum({
|
||||||
|
"2G": RANGE_2G,
|
||||||
|
"4G": RANGE_4G,
|
||||||
|
"8G": RANGE_8G,
|
||||||
|
"16G": RANGE_16G,
|
||||||
|
}, upper=True),
|
||||||
|
cv.Optional(CONF_OFF_VERTICAL): sensor.sensor_schema(
|
||||||
|
unit_of_measurement="°",
|
||||||
|
icon=ICON_RULER,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_JITTER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement="m/s²",
|
||||||
|
icon="mdi:vibrate",
|
||||||
|
accuracy_decimals=3,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACCEL_X): sensor.sensor_schema(
|
||||||
|
unit_of_measurement="m/s²",
|
||||||
|
icon="mdi:axis-x-arrow",
|
||||||
|
accuracy_decimals=3,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACCEL_Y): sensor.sensor_schema(
|
||||||
|
unit_of_measurement="m/s²",
|
||||||
|
icon="mdi:axis-y-arrow",
|
||||||
|
accuracy_decimals=3,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACCEL_Z): sensor.sensor_schema(
|
||||||
|
unit_of_measurement="m/s²",
|
||||||
|
icon="mdi:axis-z-arrow",
|
||||||
|
accuracy_decimals=3,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
}).extend(cv.polling_component_schema("100ms")).extend(i2c.i2c_device_schema(0x53))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Configure the range
|
||||||
|
cg.add(var.set_range(config[CONF_RANGE]))
|
||||||
|
|
||||||
|
# Register sensors if configured
|
||||||
|
if CONF_OFF_VERTICAL in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_OFF_VERTICAL])
|
||||||
|
cg.add(var.set_off_vertical_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_JITTER in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_JITTER])
|
||||||
|
cg.add(var.set_jitter_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_ACCEL_X in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_ACCEL_X])
|
||||||
|
cg.add(var.set_accel_x_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_ACCEL_Y in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_ACCEL_Y])
|
||||||
|
cg.add(var.set_accel_y_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_ACCEL_Z in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_ACCEL_Z])
|
||||||
|
cg.add(var.set_accel_z_sensor(sens))
|
||||||
109
components/adxl345/adxl345.cpp
Normal file
109
components/adxl345/adxl345.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "adxl345.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adxl345 {
|
||||||
|
|
||||||
|
static const char *const TAG = "adxl345";
|
||||||
|
|
||||||
|
void ADXL345Component::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ADXL345...");
|
||||||
|
|
||||||
|
if (!accel_.begin(this->address_)) {
|
||||||
|
ESP_LOGE(TAG, "Could not find ADXL345 sensor at address 0x%02X!", this->address_);
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map our range enum values to Adafruit library constants
|
||||||
|
range_t adafruit_range;
|
||||||
|
switch (this->range_) {
|
||||||
|
case 0: // RANGE_2G
|
||||||
|
adafruit_range = ADXL345_RANGE_2_G;
|
||||||
|
break;
|
||||||
|
case 1: // RANGE_4G
|
||||||
|
adafruit_range = ADXL345_RANGE_4_G;
|
||||||
|
break;
|
||||||
|
case 2: // RANGE_8G
|
||||||
|
adafruit_range = ADXL345_RANGE_8_G;
|
||||||
|
break;
|
||||||
|
case 3: // RANGE_16G
|
||||||
|
adafruit_range = ADXL345_RANGE_16_G;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
adafruit_range = ADXL345_RANGE_2_G;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
accel_.setRange(adafruit_range);
|
||||||
|
ESP_LOGD(TAG, "ADXL345 setup complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADXL345Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "ADXL345:");
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
|
||||||
|
const char* range_str;
|
||||||
|
switch (this->range_) {
|
||||||
|
case 0: range_str = "2G"; break;
|
||||||
|
case 1: range_str = "4G"; break;
|
||||||
|
case 2: range_str = "8G"; break;
|
||||||
|
case 3: range_str = "16G"; break;
|
||||||
|
default: range_str = "Unknown"; break;
|
||||||
|
}
|
||||||
|
ESP_LOGCONFIG(TAG, " Range: %s", range_str);
|
||||||
|
|
||||||
|
if (this->off_vertical_ != nullptr) {
|
||||||
|
LOG_SENSOR(" ", "Off Vertical", this->off_vertical_);
|
||||||
|
}
|
||||||
|
if (this->jitter_ != nullptr) {
|
||||||
|
LOG_SENSOR(" ", "Jitter", this->jitter_);
|
||||||
|
}
|
||||||
|
if (this->accel_x_ != nullptr) {
|
||||||
|
LOG_SENSOR(" ", "Acceleration X", this->accel_x_);
|
||||||
|
}
|
||||||
|
if (this->accel_y_ != nullptr) {
|
||||||
|
LOG_SENSOR(" ", "Acceleration Y", this->accel_y_);
|
||||||
|
}
|
||||||
|
if (this->accel_z_ != nullptr) {
|
||||||
|
LOG_SENSOR(" ", "Acceleration Z", this->accel_z_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADXL345Component::update() {
|
||||||
|
sensors_event_t event;
|
||||||
|
accel_.getEvent(&event);
|
||||||
|
|
||||||
|
// Publish raw accelerometer values if sensors are configured
|
||||||
|
if (this->accel_x_ != nullptr) {
|
||||||
|
this->accel_x_->publish_state(event.acceleration.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->accel_y_ != nullptr) {
|
||||||
|
this->accel_y_->publish_state(event.acceleration.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->accel_z_ != nullptr) {
|
||||||
|
this->accel_z_->publish_state(event.acceleration.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and publish off_vertical if sensor is configured
|
||||||
|
if (this->off_vertical_ != nullptr) {
|
||||||
|
double pitch_amount = atan(event.acceleration.y /
|
||||||
|
sqrt(pow(event.acceleration.x, 2) + pow(event.acceleration.z, 2))) * 180 / PI;
|
||||||
|
double roll_amount = atan(-1 * event.acceleration.x /
|
||||||
|
sqrt(pow(event.acceleration.y, 2) + pow(event.acceleration.z, 2))) * 180 / PI;
|
||||||
|
|
||||||
|
this->off_vertical_->publish_state(max(abs(pitch_amount), abs(roll_amount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and publish jitter if sensor is configured
|
||||||
|
if (this->jitter_ != nullptr) {
|
||||||
|
float jitter_value = abs(event.acceleration.x) + abs(event.acceleration.y) + abs(event.acceleration.z);
|
||||||
|
this->jitter_->publish_state(jitter_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace adxl345
|
||||||
|
} // namespace esphome
|
||||||
40
components/adxl345/adxl345.h
Normal file
40
components/adxl345/adxl345.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_Sensor.h>
|
||||||
|
#include <Adafruit_ADXL345_U.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adxl345 {
|
||||||
|
|
||||||
|
class ADXL345Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
ADXL345Component() : PollingComponent(100) {}
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void update() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void set_off_vertical_sensor(sensor::Sensor *off_vertical) { off_vertical_ = off_vertical; }
|
||||||
|
void set_jitter_sensor(sensor::Sensor *jitter) { jitter_ = jitter; }
|
||||||
|
void set_accel_x_sensor(sensor::Sensor *accel_x) { accel_x_ = accel_x; }
|
||||||
|
void set_accel_y_sensor(sensor::Sensor *accel_y) { accel_y_ = accel_y; }
|
||||||
|
void set_accel_z_sensor(sensor::Sensor *accel_z) { accel_z_ = accel_z; }
|
||||||
|
|
||||||
|
void set_range(uint8_t range) { range_ = range; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Adafruit_ADXL345_Unified accel_{12345};
|
||||||
|
sensor::Sensor *off_vertical_{nullptr};
|
||||||
|
sensor::Sensor *jitter_{nullptr};
|
||||||
|
sensor::Sensor *accel_x_{nullptr};
|
||||||
|
sensor::Sensor *accel_y_{nullptr};
|
||||||
|
sensor::Sensor *accel_z_{nullptr};
|
||||||
|
uint8_t range_{0}; // Default to 2G range (0)
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace adxl345
|
||||||
|
} // namespace esphome
|
||||||
4
components/adxl345/sensor.py
Normal file
4
components/adxl345/sensor.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Empty sensor.py file
|
||||||
|
# All functionality has been moved to __init__.py
|
||||||
|
|
||||||
|
from . import CONF_OFF_VERTICAL, CONF_JITTER, CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z
|
||||||
@@ -4,6 +4,8 @@ from esphome.const import CONF_ID
|
|||||||
|
|
||||||
DEPENDENCIES = ['uart']
|
DEPENDENCIES = ['uart']
|
||||||
AUTO_LOAD = ['sensor']
|
AUTO_LOAD = ['sensor']
|
||||||
|
CODEOWNERS = ["@jdillenburg"]
|
||||||
|
|
||||||
|
|
||||||
tfmini_ns = cg.esphome_ns.namespace('tfmini')
|
tfmini_ns = cg.esphome_ns.namespace('tfmini')
|
||||||
TFMiniComponent = tfmini_ns.class_('TFMiniComponent', cg.Component)
|
TFMiniComponent = tfmini_ns.class_('TFMiniComponent', cg.Component)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ packages:
|
|||||||
wifi: !include { file: packages/wifi.yaml, vars: { ssid: "garage-door-controller" }}
|
wifi: !include { file: packages/wifi.yaml, vars: { ssid: "garage-door-controller" }}
|
||||||
|
|
||||||
external_components:
|
external_components:
|
||||||
- source: github://jdillenburg/esphome@master
|
- source: github://jdillenburg/esphome@main
|
||||||
components: [ tfmini ]
|
components: [ tfmini ]
|
||||||
|
|
||||||
substitutions:
|
substitutions:
|
||||||
|
|||||||
Reference in New Issue
Block a user