Added ADXL345 external component

This commit is contained in:
John Dillenburg
2025-03-02 20:07:36 -06:00
parent d0db0b074d
commit 5e3b681308
9 changed files with 400 additions and 1 deletions

View File

@@ -6,6 +6,7 @@ Welcome to my ESPhome configuration directory. This repository contains the ESPh
| Component | Description |
|-----------|-------------|
| [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

View 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.

View 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

View 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))

View 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

View 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

View 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

View File

@@ -4,6 +4,8 @@ from esphome.const import CONF_ID
DEPENDENCIES = ['uart']
AUTO_LOAD = ['sensor']
CODEOWNERS = ["@jdillenburg"]
tfmini_ns = cg.esphome_ns.namespace('tfmini')
TFMiniComponent = tfmini_ns.class_('TFMiniComponent', cg.Component)

View File

@@ -7,7 +7,7 @@ packages:
wifi: !include { file: packages/wifi.yaml, vars: { ssid: "garage-door-controller" }}
external_components:
- source: github://jdillenburg/esphome@master
- source: github://jdillenburg/esphome@main
components: [ tfmini ]
substitutions: