118 lines
4.4 KiB
Python
118 lines
4.4 KiB
Python
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
# Copyright (C) 2020 Daniel Thompson
|
|
|
|
"""Bosch BMA421 accelerometer driver
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
"""
|
|
|
|
import bma42x
|
|
import time
|
|
import motion
|
|
from machine import Pin
|
|
|
|
# Sensor orientation definition.
|
|
# The 6 most significant bits define the indexes of the x, y, and z values
|
|
# in the acceleration tuple returned by the sensor, while the 3 least
|
|
# significant bits define their sign (1 = keep original sign, 0 = negate).
|
|
#
|
|
# Z index ─────────────────┐
|
|
# Y index ───────────────┐ │
|
|
# X index ─────────────┐ │ │
|
|
# ├┐├┐├┐
|
|
_DEFAULT_ORIENTATION = const(0b010010000)
|
|
# 1 = keep, 0 = negate │││
|
|
# X sign ───────────────────┘││
|
|
# Y sign ────────────────────┘│
|
|
# Z sign ─────────────────────┘
|
|
|
|
# Ref: BMA425 data sheet (register INT_STATUS_0)
|
|
_STATUS_MASK_WRIST_TILT = const(0b1000)
|
|
|
|
class BMA421:
|
|
"""BMA421 driver
|
|
|
|
.. automethod:: __init__
|
|
"""
|
|
def __init__(self, i2c, intr=None, orientation=_DEFAULT_ORIENTATION):
|
|
"""Configure the driver.
|
|
|
|
:param machine.I2C i2c: I2C bus used to access the sensor.
|
|
"""
|
|
self._dev = bma42x.BMA42X(i2c)
|
|
self._orientation = orientation
|
|
self._gesture_int = intr
|
|
self._gesture_event = motion.AccelGestureEvent.NONE
|
|
self.hardware_gesture_available = False
|
|
|
|
if self._gesture_int != None:
|
|
self._gesture_int.irq(trigger=Pin.IRQ_FALLING, handler=self.handle_interrupt)
|
|
|
|
def reset(self):
|
|
"""Reset and reinitialize the sensor."""
|
|
dev = self._dev
|
|
|
|
# Init, reset, wait for reset, enable I2C watchdog
|
|
dev.init()
|
|
dev.set_command_register(0xb6)
|
|
time.sleep(0.05)
|
|
dev.set_reg(bma42x.NV_CONFIG_ADDR, 6);
|
|
|
|
# Configure the sensor for basic step counting
|
|
dev.write_config_file()
|
|
dev.set_accel_enable(True)
|
|
dev.set_accel_config(odr=bma42x.OUTPUT_DATA_RATE_100HZ,
|
|
range=bma42x.ACCEL_RANGE_2G,
|
|
bandwidth=bma42x.ACCEL_NORMAL_AVG4,
|
|
perf_mode=bma42x.CIC_AVG_MODE)
|
|
dev.feature_enable(bma42x.STEP_CNTR, True)
|
|
|
|
# Set axes remapping
|
|
# This works only for hardware-based intelligence.
|
|
# Software readout is remapped manually in accel_xyz().
|
|
dev.set_remap_axes(self._orientation)
|
|
|
|
self.hardware_gesture_available = dev.get_chip_id() == bma42x.BMA425_CHIP_ID
|
|
|
|
# Enable gesture interrupts
|
|
if self.hardware_gesture_available:
|
|
dev.set_int_pin_config(int_line=bma42x.INTR1_MAP,
|
|
edge_ctrl=bma42x.LEVEL_TRIGGER,
|
|
lvl=bma42x.ACTIVE_LOW,
|
|
od=bma42x.PUSH_PULL,
|
|
output_en=True, input_en=False)
|
|
dev.feature_enable(bma42x.WRIST_WEAR, True)
|
|
dev.map_interrupt(bma42x.INTR1_MAP, bma42x.WRIST_WEAR_INT, True)
|
|
|
|
def handle_interrupt(self, pin_obj):
|
|
"""Interrupt handler for gesture events originating from the sensor"""
|
|
status = self._dev.read_int_status()
|
|
if status & _STATUS_MASK_WRIST_TILT:
|
|
self._gesture_event = motion.AccelGestureEvent.WRIST_TILT
|
|
|
|
def get_gesture_event(self):
|
|
"""Receive the latest gesture event if any"""
|
|
return self._gesture_event
|
|
|
|
def reset_gesture_event(self):
|
|
"""Call after processing the gesture event"""
|
|
self._gesture_event = motion.AccelGestureEvent.NONE
|
|
|
|
@property
|
|
def steps(self):
|
|
"""Report the number of steps counted."""
|
|
return self._dev.step_counter_output()
|
|
|
|
@steps.setter
|
|
def steps(self, value):
|
|
if value != 0:
|
|
raise ValueError()
|
|
self._dev.reset_step_counter()
|
|
|
|
def accel_xyz(self):
|
|
"""Return a triple with acceleration values"""
|
|
raw = self._dev.read_accel_xyz()
|
|
x = raw[self._orientation >> 7 & 0b11] * ((self._orientation >> 1 & 0b10) - 1)
|
|
y = raw[self._orientation >> 5 & 0b11] * ((self._orientation & 0b10) - 1)
|
|
z = raw[self._orientation >> 3 & 0b11] * ((self._orientation << 1 & 0b10) - 1)
|
|
return (x, y, z)
|