Source code for micropython_lis3mdl.lis3mdl

# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`lis3mdl`
================================================================================

MicroPython Driver for the ST LIS3MDL magnetometer


* Author: Jose D. Montoya


"""

import time
from micropython import const
from micropython_lis3mdl.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_LIS3MDL.git"


_REG_WHO_AM_I = const(0x0F)
_CTRL_REG1 = const(0x20)
_CTRL_REG2 = const(0x21)
_CTRL_REG3 = const(0x22)
_CTRL_REG4 = const(0x23)
_DATA = const(0x28)

_GAUSS_TO_UT = 100

TEMPERATURE_DISABLED = const(0b0)
TEMPERATURE_ENABLED = const(0b1)
temperature_mode_values = (TEMPERATURE_DISABLED, TEMPERATURE_ENABLED)

RATE_0_625_HZ = const(0b000000)
RATE_1_25_HZ = const(0b000010)
RATE_2_5_HZ = const(0b000100)
RATE_5_HZ = const(0b000110)
RATE_10_HZ = const(0b001000)
RATE_20_HZ = const(0b001010)
RATE_40_HZ = const(0b001100)
RATE_80_HZ = const(0b001110)
RATE_155_HZ = const(0b000001)
RATE_300_HZ = const(0b010001)
RATE_560_HZ = const(0b100001)
RATE_1000_HZ = const(0b110001)
data_rate_values = (
    RATE_0_625_HZ,
    RATE_1_25_HZ,
    RATE_2_5_HZ,
    RATE_5_HZ,
    RATE_10_HZ,
    RATE_20_HZ,
    RATE_40_HZ,
    RATE_80_HZ,
    RATE_155_HZ,
    RATE_300_HZ,
    RATE_560_HZ,
    RATE_1000_HZ,
)

SCALE_4_GAUSS = const(0b00)
SCALE_8_GAUSS = const(0b01)
SCALE_12_GAUSS = const(0b10)
SCALE_16_GAUSS = const(0b11)
scale_range_values = (SCALE_4_GAUSS, SCALE_8_GAUSS, SCALE_12_GAUSS, SCALE_16_GAUSS)
scale_range_factor = {
    SCALE_4_GAUSS: 6842,
    SCALE_8_GAUSS: 3421,
    SCALE_12_GAUSS: 2281,
    SCALE_16_GAUSS: 1711,
}

LP_DISABLED = const(0b0)
LP_ENABLED = const(0b1)
low_power_mode_values = (LP_DISABLED, LP_ENABLED)

CONTINUOUS = const(0b00)
ONE_SHOT = const(0b01)
POWER_DOWN = const(0b10)
operation_mode_values = (CONTINUOUS, ONE_SHOT, POWER_DOWN)


[docs] class LIS3MDL: """Driver for the LIS3MDL Sensor connected over I2C. :param ~machine.I2C i2c: The I2C bus the LIS3MDL is connected to. :param int address: The I2C device address. Defaults to :const:`0x1C` :raises RuntimeError: if the sensor is not found **Quickstart: Importing and using the device** Here is an example of using the :class:`LIS3MDL` class. First you will need to import the libraries to use the sensor .. code-block:: python from machine import Pin, I2C from micropython_lis3mdl import lis3mdl 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)) lis = lis3mdl.LIS3MDL(i2c) Now you have access to the attributes .. code-block:: python magx, magy, magz = lis.magnetic """ _device_id = RegisterStruct(_REG_WHO_AM_I, "B") _data_rate = CBits(6, _CTRL_REG1, 1) _scale_range = CBits(2, _CTRL_REG2, 5) _reset = CBits(1, _CTRL_REG2, 2) _low_power_mode = CBits(1, _CTRL_REG3, 5) _operation_mode = CBits(2, _CTRL_REG3, 0) _raw_magnetic_data = RegisterStruct(_DATA, "<hhh") def __init__(self, i2c, address: int = 0x1C) -> None: self._i2c = i2c self._address = address if self._device_id != 0x3D: raise RuntimeError("Failed to find the LIS3MDL sensor") self._operation_mode = CONTINUOUS self._scale_cached_factor = scale_range_factor[self._scale_range] @property def data_rate(self) -> str: """ Sensor data_rate. Data rate higher than 80 Hz needs the FAST_ODR bit activated as well as the X and Y axes operative mode selection set. Be sure to read the datasheet to select the right value according to your needs. +-----------------------------------+----------------------+ | Mode | Value | +===================================+======================+ | :py:const:`lis3mdl.RATE_0_625_HZ` | :py:const:`0b000000` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_1_25_HZ` | :py:const:`0b000010` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_2_5_HZ` | :py:const:`0b000100` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_5_HZ` | :py:const:`0b000110` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_10_HZ` | :py:const:`0b001000` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_20_HZ` | :py:const:`0b001010` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_40_HZ` | :py:const:`0b001100` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_80_HZ` | :py:const:`0b001110` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_155_HZ` | :py:const:`0b000001` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_300_HZ` | :py:const:`0b010001` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_560_HZ` | :py:const:`0b100001` | +-----------------------------------+----------------------+ | :py:const:`lis3mdl.RATE_1000_HZ` | :py:const:`0b110001` | +-----------------------------------+----------------------+ """ values = ( "RATE_0_625_HZ", "RATE_1_25_HZ", "RATE_2_5_HZ", "RATE_5_HZ", "RATE_10_HZ", "RATE_20_HZ", "RATE_40_HZ", "RATE_80_HZ", "RATE_155_HZ", "RATE_300_HZ", "RATE_560_HZ", "RATE_1000_HZ", ) 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._data_rate = value @property def scale_range(self) -> str: """ Sensor scale_range +------------------------------------+------------------+ | Mode | Value | +====================================+==================+ | :py:const:`lis3mdl.SCALE_4_GAUSS` | :py:const:`0b00` | +------------------------------------+------------------+ | :py:const:`lis3mdl.SCALE_8_GAUSS` | :py:const:`0b01` | +------------------------------------+------------------+ | :py:const:`lis3mdl.SCALE_12_GAUSS` | :py:const:`0b10` | +------------------------------------+------------------+ | :py:const:`lis3mdl.SCALE_16_GAUSS` | :py:const:`0b11` | +------------------------------------+------------------+ """ values = ("SCALE_4_GAUSS", "SCALE_8_GAUSS", "SCALE_12_GAUSS", "SCALE_16_GAUSS") 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._scale_range = value self._scale_cached_factor = scale_range_factor[value]
[docs] def reset(self) -> None: """ Reset the sensor """ self._reset = True time.sleep(0.010)
@property def low_power_mode(self) -> str: """ Sensor low_power_mode. Default value: DISABLED If ENABLED, :attr:`data_rate` is set to 0.625 Hz and the system performs, for each channel, the minimum number of averages. +---------------------------------+-----------------+ | Mode | Value | +=================================+=================+ | :py:const:`lis3mdl.LP_DISABLED` | :py:const:`0b0` | +---------------------------------+-----------------+ | :py:const:`lis3mdl.LP_ENABLED` | :py:const:`0b1` | +---------------------------------+-----------------+ """ values = ("LP_DISABLED", "LP_ENABLED") return values[self._low_power_mode] @low_power_mode.setter def low_power_mode(self, value: int) -> None: if value not in low_power_mode_values: raise ValueError("Value must be a valid low_power_mode setting") self._low_power_mode = value @property def operation_mode(self) -> str: """ Sensor operation_mode +--------------------------------+------------------+ | Mode | Value | +================================+==================+ | :py:const:`lis3mdl.CONTINUOUS` | :py:const:`0b00` | +--------------------------------+------------------+ | :py:const:`lis3mdl.ONE_SHOT` | :py:const:`0b01` | +--------------------------------+------------------+ | :py:const:`lis3mdl.POWER_DOWN` | :py:const:`0b10` | +--------------------------------+------------------+ """ values = ("CONTINUOUS", "ONE_SHOT", "POWER_DOWN") 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 magnetic(self) -> Tuple[float, float, float]: """ Magnetometer values in microteslas """ rawx, rawy, rawz = self._raw_magnetic_data x = rawx / self._scale_cached_factor * _GAUSS_TO_UT y = rawy / self._scale_cached_factor * _GAUSS_TO_UT z = rawz / self._scale_cached_factor * _GAUSS_TO_UT return x, y, z