plexus-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plexus/__init__.py +31 -0
- plexus/__main__.py +4 -0
- plexus/adapters/__init__.py +122 -0
- plexus/adapters/base.py +409 -0
- plexus/adapters/ble.py +257 -0
- plexus/adapters/can.py +439 -0
- plexus/adapters/can_detect.py +174 -0
- plexus/adapters/mavlink.py +642 -0
- plexus/adapters/mavlink_detect.py +192 -0
- plexus/adapters/modbus.py +622 -0
- plexus/adapters/mqtt.py +350 -0
- plexus/adapters/opcua.py +607 -0
- plexus/adapters/registry.py +206 -0
- plexus/adapters/serial_adapter.py +547 -0
- plexus/buffer.py +257 -0
- plexus/cameras/__init__.py +57 -0
- plexus/cameras/auto.py +239 -0
- plexus/cameras/base.py +189 -0
- plexus/cameras/picamera.py +171 -0
- plexus/cameras/usb.py +143 -0
- plexus/cli.py +783 -0
- plexus/client.py +465 -0
- plexus/config.py +169 -0
- plexus/connector.py +666 -0
- plexus/deps.py +246 -0
- plexus/detect.py +1238 -0
- plexus/importers/__init__.py +25 -0
- plexus/importers/rosbag.py +778 -0
- plexus/sensors/__init__.py +118 -0
- plexus/sensors/ads1115.py +164 -0
- plexus/sensors/adxl345.py +179 -0
- plexus/sensors/auto.py +290 -0
- plexus/sensors/base.py +412 -0
- plexus/sensors/bh1750.py +102 -0
- plexus/sensors/bme280.py +241 -0
- plexus/sensors/gps.py +317 -0
- plexus/sensors/ina219.py +149 -0
- plexus/sensors/magnetometer.py +239 -0
- plexus/sensors/mpu6050.py +162 -0
- plexus/sensors/sht3x.py +139 -0
- plexus/sensors/spi_scan.py +164 -0
- plexus/sensors/system.py +261 -0
- plexus/sensors/vl53l0x.py +109 -0
- plexus/streaming.py +743 -0
- plexus/tui.py +642 -0
- plexus_python-0.1.0.dist-info/METADATA +470 -0
- plexus_python-0.1.0.dist-info/RECORD +50 -0
- plexus_python-0.1.0.dist-info/WHEEL +4 -0
- plexus_python-0.1.0.dist-info/entry_points.txt +2 -0
- plexus_python-0.1.0.dist-info/licenses/LICENSE +190 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plexus Sensor Drivers
|
|
3
|
+
|
|
4
|
+
Pre-built drivers for common sensors that stream data to Plexus.
|
|
5
|
+
|
|
6
|
+
Quick Start:
|
|
7
|
+
from plexus import Plexus
|
|
8
|
+
from plexus.sensors import MPU6050
|
|
9
|
+
|
|
10
|
+
px = Plexus()
|
|
11
|
+
imu = MPU6050()
|
|
12
|
+
|
|
13
|
+
while True:
|
|
14
|
+
for reading in imu.read():
|
|
15
|
+
px.send(reading.metric, reading.value)
|
|
16
|
+
|
|
17
|
+
With SensorHub (recommended):
|
|
18
|
+
from plexus import Plexus
|
|
19
|
+
from plexus.sensors import SensorHub, MPU6050, BME280
|
|
20
|
+
|
|
21
|
+
hub = SensorHub()
|
|
22
|
+
hub.add(MPU6050(sample_rate=100))
|
|
23
|
+
hub.add(BME280(sample_rate=1))
|
|
24
|
+
hub.run(Plexus())
|
|
25
|
+
|
|
26
|
+
Auto-detection:
|
|
27
|
+
from plexus import Plexus
|
|
28
|
+
from plexus.sensors import auto_sensors
|
|
29
|
+
|
|
30
|
+
hub = auto_sensors() # Finds all connected sensors
|
|
31
|
+
hub.run(Plexus())
|
|
32
|
+
|
|
33
|
+
Supported Sensors:
|
|
34
|
+
- MPU6050: 6-axis IMU (accelerometer + gyroscope)
|
|
35
|
+
- MPU9250: 9-axis IMU (accelerometer + gyroscope + magnetometer)
|
|
36
|
+
- BME280: Environmental (temperature, humidity, pressure)
|
|
37
|
+
- INA219: Current/power monitor (voltage, current, power)
|
|
38
|
+
- SHT3x: Precision temperature + humidity (with CRC verification)
|
|
39
|
+
- BH1750: Ambient light sensor (1-65535 lux)
|
|
40
|
+
- VL53L0X: Time-of-flight distance sensor (30-2000mm)
|
|
41
|
+
- ADS1115: 16-bit ADC (4 channels, programmable gain)
|
|
42
|
+
- QMC5883L: 3-axis magnetometer / compass
|
|
43
|
+
- HMC5883L: 3-axis magnetometer / compass
|
|
44
|
+
- ADXL345: 3-axis accelerometer (I2C or SPI)
|
|
45
|
+
- GPSSensor: GPS receiver (lat, lon, altitude, speed via NMEA serial)
|
|
46
|
+
- SystemSensor: System health (CPU temp, memory, disk, load)
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from .base import BaseSensor, SensorReading, SensorHub
|
|
50
|
+
from .mpu6050 import MPU6050, MPU9250
|
|
51
|
+
from .bme280 import BME280
|
|
52
|
+
from .ina219 import INA219
|
|
53
|
+
from .sht3x import SHT3x
|
|
54
|
+
from .bh1750 import BH1750
|
|
55
|
+
from .vl53l0x import VL53L0X
|
|
56
|
+
from .ads1115 import ADS1115
|
|
57
|
+
from .magnetometer import QMC5883L, HMC5883L
|
|
58
|
+
from .adxl345 import ADXL345
|
|
59
|
+
from .gps import GPSSensor
|
|
60
|
+
from .auto import scan_sensors, auto_sensors, scan_i2c, DetectedSensor
|
|
61
|
+
from .system import SystemSensor
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
from .spi_scan import scan_spi, scan_spi_buses, SPISensorMatch
|
|
65
|
+
except ImportError:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
# Base classes
|
|
70
|
+
"BaseSensor",
|
|
71
|
+
"SensorReading",
|
|
72
|
+
"SensorHub",
|
|
73
|
+
# I2C sensors
|
|
74
|
+
"MPU6050",
|
|
75
|
+
"MPU9250",
|
|
76
|
+
"BME280",
|
|
77
|
+
"INA219",
|
|
78
|
+
"SHT3x",
|
|
79
|
+
"BH1750",
|
|
80
|
+
"VL53L0X",
|
|
81
|
+
"ADS1115",
|
|
82
|
+
"QMC5883L",
|
|
83
|
+
"HMC5883L",
|
|
84
|
+
"ADXL345",
|
|
85
|
+
# Serial sensors
|
|
86
|
+
"GPSSensor",
|
|
87
|
+
# System
|
|
88
|
+
"SystemSensor",
|
|
89
|
+
# Auto-detection
|
|
90
|
+
"scan_sensors",
|
|
91
|
+
"auto_sensors",
|
|
92
|
+
"scan_i2c",
|
|
93
|
+
"DetectedSensor",
|
|
94
|
+
"scan_spi",
|
|
95
|
+
"scan_spi_buses",
|
|
96
|
+
"SPISensorMatch",
|
|
97
|
+
# Registry
|
|
98
|
+
"SENSOR_REGISTRY",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
# Maps sensor names to driver classes (used by config and CLI)
|
|
102
|
+
SENSOR_REGISTRY = {
|
|
103
|
+
# Named sensors (no I2C address needed)
|
|
104
|
+
"system": SystemSensor,
|
|
105
|
+
"gps": GPSSensor,
|
|
106
|
+
# I2C sensors
|
|
107
|
+
"mpu6050": MPU6050,
|
|
108
|
+
"mpu9250": MPU9250,
|
|
109
|
+
"bme280": BME280,
|
|
110
|
+
"ina219": INA219,
|
|
111
|
+
"sht3x": SHT3x,
|
|
112
|
+
"bh1750": BH1750,
|
|
113
|
+
"vl53l0x": VL53L0X,
|
|
114
|
+
"ads1115": ADS1115,
|
|
115
|
+
"qmc5883l": QMC5883L,
|
|
116
|
+
"hmc5883l": HMC5883L,
|
|
117
|
+
"adxl345": ADXL345,
|
|
118
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ADS1115 16-bit ADC sensor driver.
|
|
3
|
+
|
|
4
|
+
The ADS1115 is a precision 16-bit ADC with 4 single-ended or 2 differential
|
|
5
|
+
channels. Programmable gain amplifier (PGA) and data rate.
|
|
6
|
+
Communicates via I2C at address 0x48–0x4B (configurable via ADDR pin).
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from plexus.sensors import ADS1115
|
|
10
|
+
|
|
11
|
+
sensor = ADS1115()
|
|
12
|
+
for reading in sensor.read():
|
|
13
|
+
print(f"{reading.metric}: {reading.value}")
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import time
|
|
17
|
+
from typing import List, Optional
|
|
18
|
+
from .base import BaseSensor, SensorReading
|
|
19
|
+
|
|
20
|
+
ADS1115_ADDR = 0x48
|
|
21
|
+
|
|
22
|
+
# Register addresses
|
|
23
|
+
REG_CONVERSION = 0x00
|
|
24
|
+
REG_CONFIG = 0x01
|
|
25
|
+
|
|
26
|
+
# Config register bits
|
|
27
|
+
# OS: Start single conversion
|
|
28
|
+
OS_SINGLE = 0x8000
|
|
29
|
+
# MUX: Input multiplexer
|
|
30
|
+
MUX_AIN0 = 0x4000 # AIN0 vs GND
|
|
31
|
+
MUX_AIN1 = 0x5000 # AIN1 vs GND
|
|
32
|
+
MUX_AIN2 = 0x6000 # AIN2 vs GND
|
|
33
|
+
MUX_AIN3 = 0x7000 # AIN3 vs GND
|
|
34
|
+
# PGA: Programmable gain (full-scale voltage)
|
|
35
|
+
PGA_6144 = 0x0000 # ±6.144V (LSB = 187.5µV)
|
|
36
|
+
PGA_4096 = 0x0200 # ±4.096V (LSB = 125µV)
|
|
37
|
+
PGA_2048 = 0x0400 # ±2.048V (LSB = 62.5µV) — default
|
|
38
|
+
PGA_1024 = 0x0600 # ±1.024V
|
|
39
|
+
PGA_0512 = 0x0800 # ±0.512V
|
|
40
|
+
PGA_0256 = 0x0A00 # ±0.256V
|
|
41
|
+
# MODE: Operating mode
|
|
42
|
+
MODE_SINGLE = 0x0100 # Single-shot
|
|
43
|
+
# DR: Data rate
|
|
44
|
+
DR_128SPS = 0x0080 # 128 samples per second
|
|
45
|
+
# COMP: Disable comparator
|
|
46
|
+
COMP_DISABLE = 0x0003
|
|
47
|
+
|
|
48
|
+
MUX_CHANNELS = [MUX_AIN0, MUX_AIN1, MUX_AIN2, MUX_AIN3]
|
|
49
|
+
|
|
50
|
+
# Voltage per LSB for each PGA setting
|
|
51
|
+
PGA_LSB = {
|
|
52
|
+
PGA_6144: 0.0001875,
|
|
53
|
+
PGA_4096: 0.000125,
|
|
54
|
+
PGA_2048: 0.0000625,
|
|
55
|
+
PGA_1024: 0.00003125,
|
|
56
|
+
PGA_0512: 0.000015625,
|
|
57
|
+
PGA_0256: 0.0000078125,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ADS1115(BaseSensor):
|
|
62
|
+
"""
|
|
63
|
+
ADS1115 16-bit ADC driver.
|
|
64
|
+
|
|
65
|
+
Provides:
|
|
66
|
+
- adc_ch0 through adc_ch3: Voltage readings in volts (single-ended)
|
|
67
|
+
|
|
68
|
+
Default gain: ±4.096V (suitable for 3.3V/5V systems)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
name = "ADS1115"
|
|
72
|
+
description = "16-bit ADC (4 channels, programmable gain)"
|
|
73
|
+
metrics = ["adc_ch0", "adc_ch1", "adc_ch2", "adc_ch3"]
|
|
74
|
+
i2c_addresses = [0x48, 0x49, 0x4A, 0x4B]
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
address: int = ADS1115_ADDR,
|
|
79
|
+
bus: int = 1,
|
|
80
|
+
gain: int = PGA_4096,
|
|
81
|
+
channels: Optional[List[int]] = None,
|
|
82
|
+
sample_rate: float = 10.0,
|
|
83
|
+
prefix: str = "",
|
|
84
|
+
tags: Optional[dict] = None,
|
|
85
|
+
):
|
|
86
|
+
super().__init__(sample_rate=sample_rate, prefix=prefix, tags=tags)
|
|
87
|
+
self.address = address
|
|
88
|
+
self.bus_num = bus
|
|
89
|
+
self.gain = gain
|
|
90
|
+
self.channels = channels if channels is not None else [0, 1, 2, 3]
|
|
91
|
+
self._bus = None
|
|
92
|
+
self._lsb = PGA_LSB.get(gain, 0.000125)
|
|
93
|
+
|
|
94
|
+
def setup(self) -> None:
|
|
95
|
+
try:
|
|
96
|
+
from smbus2 import SMBus
|
|
97
|
+
except ImportError:
|
|
98
|
+
raise ImportError(
|
|
99
|
+
"smbus2 is required for ADS1115. Install with: pip install smbus2"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
self._bus = SMBus(self.bus_num)
|
|
103
|
+
|
|
104
|
+
def cleanup(self) -> None:
|
|
105
|
+
if self._bus:
|
|
106
|
+
self._bus.close()
|
|
107
|
+
self._bus = None
|
|
108
|
+
|
|
109
|
+
def _read_channel(self, channel: int) -> float:
|
|
110
|
+
"""Read a single ADC channel and return voltage."""
|
|
111
|
+
config = (
|
|
112
|
+
OS_SINGLE |
|
|
113
|
+
MUX_CHANNELS[channel] |
|
|
114
|
+
self.gain |
|
|
115
|
+
MODE_SINGLE |
|
|
116
|
+
DR_128SPS |
|
|
117
|
+
COMP_DISABLE
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Write config to start conversion
|
|
121
|
+
high = (config >> 8) & 0xFF
|
|
122
|
+
low = config & 0xFF
|
|
123
|
+
self._bus.write_i2c_block_data(self.address, REG_CONFIG, [high, low])
|
|
124
|
+
|
|
125
|
+
# Wait for conversion (128 SPS = ~8ms per sample)
|
|
126
|
+
time.sleep(0.009)
|
|
127
|
+
|
|
128
|
+
# Read conversion result
|
|
129
|
+
data = self._bus.read_i2c_block_data(self.address, REG_CONVERSION, 2)
|
|
130
|
+
raw = (data[0] << 8) | data[1]
|
|
131
|
+
|
|
132
|
+
# Convert to signed
|
|
133
|
+
if raw > 32767:
|
|
134
|
+
raw -= 65536
|
|
135
|
+
|
|
136
|
+
return raw * self._lsb
|
|
137
|
+
|
|
138
|
+
def read(self) -> List[SensorReading]:
|
|
139
|
+
if self._bus is None:
|
|
140
|
+
self.setup()
|
|
141
|
+
|
|
142
|
+
readings = []
|
|
143
|
+
for ch in self.channels:
|
|
144
|
+
if 0 <= ch <= 3:
|
|
145
|
+
voltage = self._read_channel(ch)
|
|
146
|
+
readings.append(
|
|
147
|
+
SensorReading(f"adc_ch{ch}", round(voltage, 4))
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return readings
|
|
151
|
+
|
|
152
|
+
def is_available(self) -> bool:
|
|
153
|
+
try:
|
|
154
|
+
from smbus2 import SMBus
|
|
155
|
+
|
|
156
|
+
bus = SMBus(self.bus_num)
|
|
157
|
+
# Read config register — default is 0x8583
|
|
158
|
+
data = bus.read_i2c_block_data(self.address, REG_CONFIG, 2)
|
|
159
|
+
bus.close()
|
|
160
|
+
config = (data[0] << 8) | data[1]
|
|
161
|
+
# Check OS bit is set (no conversion in progress)
|
|
162
|
+
return (config & 0x8000) != 0
|
|
163
|
+
except Exception:
|
|
164
|
+
return False
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ADXL345 3-axis accelerometer driver.
|
|
3
|
+
|
|
4
|
+
Supports both I2C and SPI interfaces for the ADXL345 digital accelerometer.
|
|
5
|
+
Measures acceleration on X, Y, Z axes in units of g (9.81 m/s²).
|
|
6
|
+
|
|
7
|
+
Usage (I2C):
|
|
8
|
+
from plexus.sensors import ADXL345
|
|
9
|
+
|
|
10
|
+
accel = ADXL345(bus_type="i2c", address=0x53)
|
|
11
|
+
accel.setup()
|
|
12
|
+
for reading in accel.read():
|
|
13
|
+
print(f"{reading.metric}: {reading.value}")
|
|
14
|
+
|
|
15
|
+
Usage (SPI):
|
|
16
|
+
accel = ADXL345(bus_type="spi", spi_bus=0, spi_cs=0)
|
|
17
|
+
accel.setup()
|
|
18
|
+
for reading in accel.read():
|
|
19
|
+
print(f"{reading.metric}: {reading.value}")
|
|
20
|
+
|
|
21
|
+
With SensorHub:
|
|
22
|
+
from plexus.sensors import SensorHub, ADXL345
|
|
23
|
+
hub = SensorHub()
|
|
24
|
+
hub.add(ADXL345())
|
|
25
|
+
hub.run(Plexus())
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import struct
|
|
29
|
+
import time
|
|
30
|
+
from typing import Dict, List, Optional
|
|
31
|
+
|
|
32
|
+
from .base import BaseSensor, SensorReading
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ADXL345 registers
|
|
36
|
+
_REG_DEVID = 0x00
|
|
37
|
+
_REG_BW_RATE = 0x2C
|
|
38
|
+
_REG_POWER_CTL = 0x2D
|
|
39
|
+
_REG_DATA_FORMAT = 0x31
|
|
40
|
+
_REG_DATAX0 = 0x32
|
|
41
|
+
|
|
42
|
+
# Expected chip ID
|
|
43
|
+
_CHIP_ID = 0xE5
|
|
44
|
+
|
|
45
|
+
# Scale factor: 4mg/LSB in full resolution mode
|
|
46
|
+
_SCALE_FACTOR = 0.004
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ADXL345(BaseSensor):
|
|
50
|
+
"""ADXL345 3-axis accelerometer (I2C or SPI)."""
|
|
51
|
+
|
|
52
|
+
name = "ADXL345"
|
|
53
|
+
description = "3-axis accelerometer (±2g/±4g/±8g/±16g)"
|
|
54
|
+
metrics = ["accel_x", "accel_y", "accel_z"]
|
|
55
|
+
i2c_addresses = [0x53, 0x1D]
|
|
56
|
+
spi_devices = [(0, 0)]
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
bus_type: str = "i2c",
|
|
61
|
+
address: int = 0x53,
|
|
62
|
+
bus: int = 1,
|
|
63
|
+
spi_bus: int = 0,
|
|
64
|
+
spi_cs: int = 0,
|
|
65
|
+
sample_rate: float = 100.0,
|
|
66
|
+
prefix: str = "",
|
|
67
|
+
tags: Optional[Dict[str, str]] = None,
|
|
68
|
+
):
|
|
69
|
+
"""
|
|
70
|
+
Args:
|
|
71
|
+
bus_type: "i2c" or "spi"
|
|
72
|
+
address: I2C address (0x53 or 0x1D)
|
|
73
|
+
bus: I2C bus number (usually 1 on Raspberry Pi)
|
|
74
|
+
spi_bus: SPI bus number
|
|
75
|
+
spi_cs: SPI chip select
|
|
76
|
+
sample_rate: Readings per second (Hz)
|
|
77
|
+
prefix: Prefix for metric names
|
|
78
|
+
tags: Tags to add to all readings
|
|
79
|
+
"""
|
|
80
|
+
super().__init__(sample_rate=sample_rate, prefix=prefix, tags=tags)
|
|
81
|
+
self.bus_type = bus_type
|
|
82
|
+
self.address = address
|
|
83
|
+
self.bus_num = bus
|
|
84
|
+
self.spi_bus = spi_bus
|
|
85
|
+
self.spi_cs = spi_cs
|
|
86
|
+
self._dev = None
|
|
87
|
+
|
|
88
|
+
def _read_reg(self, reg: int, length: int = 1) -> bytes:
|
|
89
|
+
"""Read register(s) via I2C or SPI."""
|
|
90
|
+
if self.bus_type == "spi":
|
|
91
|
+
# SPI read: bit 7 = read, bit 6 = multi-byte
|
|
92
|
+
cmd = reg | 0x80
|
|
93
|
+
if length > 1:
|
|
94
|
+
cmd |= 0x40
|
|
95
|
+
tx = [cmd] + [0x00] * length
|
|
96
|
+
rx = self._dev.xfer2(tx)
|
|
97
|
+
return bytes(rx[1:])
|
|
98
|
+
else:
|
|
99
|
+
if length == 1:
|
|
100
|
+
return bytes([self._dev.read_byte_data(self.address, reg)])
|
|
101
|
+
return bytes(self._dev.read_i2c_block_data(self.address, reg, length))
|
|
102
|
+
|
|
103
|
+
def _write_reg(self, reg: int, value: int):
|
|
104
|
+
"""Write a register via I2C or SPI."""
|
|
105
|
+
if self.bus_type == "spi":
|
|
106
|
+
self._dev.xfer2([reg, value])
|
|
107
|
+
else:
|
|
108
|
+
self._dev.write_byte_data(self.address, reg, value)
|
|
109
|
+
|
|
110
|
+
def setup(self) -> None:
|
|
111
|
+
"""Initialize the ADXL345."""
|
|
112
|
+
if self.bus_type == "spi":
|
|
113
|
+
import spidev
|
|
114
|
+
self._dev = spidev.SpiDev()
|
|
115
|
+
self._dev.open(self.spi_bus, self.spi_cs)
|
|
116
|
+
self._dev.mode = 3
|
|
117
|
+
self._dev.max_speed_hz = 1000000
|
|
118
|
+
else:
|
|
119
|
+
from smbus2 import SMBus
|
|
120
|
+
self._dev = SMBus(self.bus_num)
|
|
121
|
+
|
|
122
|
+
# Verify chip ID
|
|
123
|
+
chip_id = self._read_reg(_REG_DEVID)[0]
|
|
124
|
+
if chip_id != _CHIP_ID:
|
|
125
|
+
raise RuntimeError(
|
|
126
|
+
f"ADXL345 not found (got chip ID 0x{chip_id:02X}, expected 0x{_CHIP_ID:02X})"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Configure: 100Hz output rate
|
|
130
|
+
self._write_reg(_REG_BW_RATE, 0x0A)
|
|
131
|
+
# Full resolution, ±2g range
|
|
132
|
+
self._write_reg(_REG_DATA_FORMAT, 0x08)
|
|
133
|
+
# Start measurement
|
|
134
|
+
self._write_reg(_REG_POWER_CTL, 0x08)
|
|
135
|
+
|
|
136
|
+
def cleanup(self) -> None:
|
|
137
|
+
"""Clean up resources."""
|
|
138
|
+
if self._dev:
|
|
139
|
+
if self.bus_type == "spi":
|
|
140
|
+
self._dev.close()
|
|
141
|
+
else:
|
|
142
|
+
self._dev.close()
|
|
143
|
+
self._dev = None
|
|
144
|
+
|
|
145
|
+
def read(self) -> List[SensorReading]:
|
|
146
|
+
"""Read acceleration on all three axes."""
|
|
147
|
+
if not self._dev:
|
|
148
|
+
return []
|
|
149
|
+
|
|
150
|
+
now = time.time()
|
|
151
|
+
raw = self._read_reg(_REG_DATAX0, 6)
|
|
152
|
+
x, y, z = struct.unpack("<hhh", raw)
|
|
153
|
+
|
|
154
|
+
return [
|
|
155
|
+
SensorReading("accel_x", round(x * _SCALE_FACTOR, 4), now),
|
|
156
|
+
SensorReading("accel_y", round(y * _SCALE_FACTOR, 4), now),
|
|
157
|
+
SensorReading("accel_z", round(z * _SCALE_FACTOR, 4), now),
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
def is_available(self) -> bool:
|
|
161
|
+
"""Check if ADXL345 is connected."""
|
|
162
|
+
try:
|
|
163
|
+
if self.bus_type == "spi":
|
|
164
|
+
import spidev
|
|
165
|
+
dev = spidev.SpiDev()
|
|
166
|
+
dev.open(self.spi_bus, self.spi_cs)
|
|
167
|
+
dev.mode = 3
|
|
168
|
+
dev.max_speed_hz = 1000000
|
|
169
|
+
resp = dev.xfer2([_REG_DEVID | 0x80, 0x00])
|
|
170
|
+
dev.close()
|
|
171
|
+
return resp[1] == _CHIP_ID
|
|
172
|
+
else:
|
|
173
|
+
from smbus2 import SMBus
|
|
174
|
+
bus = SMBus(self.bus_num)
|
|
175
|
+
chip_id = bus.read_byte_data(self.address, _REG_DEVID)
|
|
176
|
+
bus.close()
|
|
177
|
+
return chip_id == _CHIP_ID
|
|
178
|
+
except Exception:
|
|
179
|
+
return False
|