mirror of
https://github.com/jdillenburg/esphome.git
synced 2026-01-08 22:40:39 -07: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 |
|
||||
|-----------|-------------|
|
||||
| [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
|
||||
|
||||
|
||||
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']
|
||||
AUTO_LOAD = ['sensor']
|
||||
CODEOWNERS = ["@jdillenburg"]
|
||||
|
||||
|
||||
tfmini_ns = cg.esphome_ns.namespace('tfmini')
|
||||
TFMiniComponent = tfmini_ns.class_('TFMiniComponent', cg.Component)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user