# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`mma8451`
================================================================================
MicroPython module for the MMA8451 3 axis accelerometer
* Author(s): Jose D. Montoya
"""
from micropython import const
from micropython_mma8451.i2c_helpers import CBits, RegisterStruct
try:
from typing import Tuple
except ImportError:
pass
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/jposada202020/MicroPython_MMA8451.git"
_REG_WHOAMI = const(0x0D)
_DATA = const(0x01)
_XYZ_DATA_CFG = const(0x0E)
_CTRL_REG1 = const(0x2A)
_HP_FILTER_CUTOFF = const(0x2F)
_GRAVITY = 9.80665
STANDBY_MODE = const(0b0)
ACTIVE_MODE = const(0b1)
operation_mode_values = (STANDBY_MODE, ACTIVE_MODE)
RANGE_2G = const(0b00)
RANGE_4G = const(0b01)
RANGE_8G = const(0b10)
scale_range_values = (RANGE_2G, RANGE_4G, RANGE_8G)
scale_conversion = {RANGE_2G: 4096.0, RANGE_4G: 2048.0, RANGE_8G: 1024.0}
DATARATE_800HZ = const(0b000)
DATARATE_400HZ = const(0b001)
DATARATE_200HZ = const(0b010)
DATARATE_100HZ = const(0b011)
DATARATE_50HZ = const(0b100)
DATARATE_12_5HZ = const(0b101)
DATARATE_6_25HZ = const(0b110)
DATARATE_1_56HZ = const(0b111)
data_rate_values = (
DATARATE_800HZ,
DATARATE_400HZ,
DATARATE_200HZ,
DATARATE_100HZ,
DATARATE_50HZ,
DATARATE_12_5HZ,
DATARATE_6_25HZ,
DATARATE_1_56HZ,
)
HPF_DISABLED = const(0b0)
HPF_ENABLED = const(0b1)
high_pass_filter_values = (HPF_DISABLED, HPF_ENABLED)
CUTOFF_16HZ = const(0b00)
CUTOFF_8HZ = const(0b01)
CUTOFF_4HZ = const(0b10)
CUTOFF_2HZ = const(0b11)
high_pass_filter_cutoff_values = (CUTOFF_16HZ, CUTOFF_8HZ, CUTOFF_4HZ, CUTOFF_2HZ)
[docs]
class MMA8451:
"""Driver for the MMA8451 Sensor connected over I2C.
:param ~machine.I2C i2c: The I2C bus the MMA8451 is connected to.
:param int address: The I2C device address. Defaults to :const:`0x1D`
:raises RuntimeError: if the sensor is not found
**Quickstart: Importing and using the device**
Here is an example of using the :class:`MMA8451` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
from machine import Pin, I2C
from micropython_mma8451 import mma8451
Once this is done you can define your `machine.I2C` object and define your sensor object
.. code-block:: python
i2c = I2C(1, sda=Pin(2), scl=Pin(3))
mma8451 = mma8451.MMA8451(i2c)
Now you have access to the attributes
.. code-block:: python
accx, accy, accz = mma8451.acceleration
"""
_device_id = RegisterStruct(_REG_WHOAMI, "B")
_raw_data = RegisterStruct(_DATA, ">hhh")
_operation_mode = CBits(1, _CTRL_REG1, 0)
_scale_range = CBits(2, _XYZ_DATA_CFG, 0)
_data_rate = CBits(2, _CTRL_REG1, 4)
_high_pass_filter = CBits(1, _XYZ_DATA_CFG, 4)
_high_pass_filter_cutoff = CBits(2, _HP_FILTER_CUTOFF, 0)
def __init__(self, i2c, address: int = 0x1D) -> None:
self._i2c = i2c
self._address = address
if self._device_id != 0x1A:
raise RuntimeError("Failed to find MMA8451")
self._operation_mode = ACTIVE_MODE
self._scale_range_cached = self._scale_range
@property
def acceleration(self) -> Tuple[float, float, float]:
"""
Acceleration measured by the sensor in :math:`m/s^2`.
"""
x, y, z = self._raw_data
x >>= 2
y >>= 2
z >>= 2
divisor = scale_conversion[self._scale_range_cached]
return x / divisor * _GRAVITY, y / divisor * _GRAVITY, z / divisor * _GRAVITY
@property
def operation_mode(self) -> str:
"""
Sensor operation_mode
+----------------------------------+-----------------+
| Mode | Value |
+==================================+=================+
| :py:const:`mma8451.STANDBY_MODE` | :py:const:`0b0` |
+----------------------------------+-----------------+
| :py:const:`mma8451.ACTIVE_MODE` | :py:const:`0b1` |
+----------------------------------+-----------------+
"""
values = ("STANDBY_MODE", "ACTIVE_MODE")
return values[self._operation_mode]
@operation_mode.setter
def operation_mode(self, value: int) -> None:
if value not in operation_mode_values:
raise ValueError("Value must be a valid operation_mode setting")
self._operation_mode = value
@property
def scale_range(self) -> str:
"""
Sensor scale_range
+------------------------------+------------------+
| Mode | Value |
+==============================+==================+
| :py:const:`mma8451.RANGE_2G` | :py:const:`0b00` |
+------------------------------+------------------+
| :py:const:`mma8451.RANGE_4G` | :py:const:`0b01` |
+------------------------------+------------------+
| :py:const:`mma8451.RANGE_8G` | :py:const:`0b10` |
+------------------------------+------------------+
"""
values = ("RANGE_2G", "RANGE_4G", "RANGE_8G")
return values[self._scale_range]
@scale_range.setter
def scale_range(self, value: int) -> None:
if value not in scale_range_values:
raise ValueError("Value must be a valid scale_range setting")
self._operation_mode = STANDBY_MODE
self._scale_range = value
self._scale_range_cached = value
self._operation_mode = ACTIVE_MODE
@property
def data_rate(self) -> str:
"""
Sensor data_rate
+-------------------------------------+-------------------+
| Mode | Value |
+=====================================+===================+
| :py:const:`mma8451.DATARATE_800HZ` | :py:const:`0b000` |
+-------------------------------------+-------------------+
| :py:const:`mma8451.DATARATE_400HZ` | :py:const:`0b001` |
+-------------------------------------+-------------------+
| :py:const:`mma8451.DATARATE_200HZ` | :py:const:`0b010` |
+-------------------------------------+-------------------+
| :py:const:`mma8451.DATARATE_100HZ` | :py:const:`0b011` |
+-------------------------------------+-------------------+
| :py:const:`mma8451.DATARATE_50HZ` | :py:const:`0b100` |
+-------------------------------------+-------------------+
| :py:const:`mma8451.DATARATE_12_5HZ` | :py:const:`0b101` |
+-------------------------------------+-------------------+
| :py:const:`mma8451.DATARATE_6_25HZ` | :py:const:`0b110` |
+-------------------------------------+-------------------+
| :py:const:`mma8451.DATARATE_1_56HZ` | :py:const:`0b111` |
+-------------------------------------+-------------------+
"""
values = (
"DATARATE_800HZ",
"DATARATE_400HZ",
"DATARATE_200HZ",
"DATARATE_100HZ",
"DATARATE_50HZ",
"DATARATE_12_5HZ",
"DATARATE_6_25HZ",
"DATARATE_1_56HZ",
)
return values[self._data_rate]
@data_rate.setter
def data_rate(self, value: int) -> None:
if value not in data_rate_values:
raise ValueError("Value must be a valid data_rate setting")
self._operation_mode = STANDBY_MODE
self._data_rate = value
self._operation_mode = ACTIVE_MODE
@property
def high_pass_filter(self) -> str:
"""
Sensor high_pass_filter
+----------------------------------+-----------------+
| Mode | Value |
+==================================+=================+
| :py:const:`mma8451.HPF_DISABLED` | :py:const:`0b0` |
+----------------------------------+-----------------+
| :py:const:`mma8451.HPF_ENABLED` | :py:const:`0b1` |
+----------------------------------+-----------------+
"""
values = ("HPF_DISABLED", "HPF_ENABLED")
return values[self._high_pass_filter]
@high_pass_filter.setter
def high_pass_filter(self, value: int) -> None:
if value not in high_pass_filter_values:
raise ValueError("Value must be a valid high_pass_filter setting")
self._operation_mode = STANDBY_MODE
self._high_pass_filter = value
self._operation_mode = ACTIVE_MODE
@property
def high_pass_filter_cutoff(self) -> str:
"""
Sensor high_pass_filter_cutoff sets the high-pass filter cutoff
frequency for removal of the offset and slower changing
acceleration data. In order to filter the acceleration data
:attr:`high_pass_filter` must be enabled.
+---------------------------------+------------------+
| Mode | Value |
+=================================+==================+
| :py:const:`mma8451.CUTOFF_16HZ` | :py:const:`0b00` |
+---------------------------------+------------------+
| :py:const:`mma8451.CUTOFF_8HZ` | :py:const:`0b01` |
+---------------------------------+------------------+
| :py:const:`mma8451.CUTOFF_4HZ` | :py:const:`0b10` |
+---------------------------------+------------------+
| :py:const:`mma8451.CUTOFF_2HZ` | :py:const:`0b11` |
+---------------------------------+------------------+
"""
values = ("CUTOFF_16HZ", "CUTOFF_8HZ", "CUTOFF_4HZ", "CUTOFF_2HZ")
return values[self._high_pass_filter_cutoff]
@high_pass_filter_cutoff.setter
def high_pass_filter_cutoff(self, value: int) -> None:
if value not in high_pass_filter_cutoff_values:
raise ValueError("Value must be a valid high_pass_filter_cutoff setting")
self._operation_mode = STANDBY_MODE
self._high_pass_filter_cutoff = value
self._operation_mode = ACTIVE_MODE