Source code for micropython_mmc5603.mmc5603
# SPDX-FileCopyrightText: Copyright (c) 2022 ladyada for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`mmc5603`
================================================================================
MicroPython driver for the Memsic MMC5603 Magnetometer
* Author(s): ladyada, Jose D. Montoya
"""
import time
from micropython import const
from micropython_mmc5603.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_MMC5603.git"
_REG_WHOIAM = const(0x39)
_DATA = const(0x00)
_TEMP = const(0x09)
_STATUS_REG = const(0x18)
_ODR_REG = const(0x1A)
_CTRL_REG0 = const(0x1B)
_CTRL_REG1 = const(0x1C)
_CTRL_REG2 = const(0x1D)
MT_6_6ms = const(0b00)
MT_3_5ms = const(0b01)
MT_2_0ms = const(0b10)
MT_1_2ms = const(0b11)
measure_time_values = (MT_6_6ms, MT_3_5ms, MT_2_0ms, MT_1_2ms)
[docs]
class MMC5603:
"""Driver for the MMC5603 Sensor connected over I2C.
:param ~machine.I2C i2c: The I2C bus the MMC5603 is connected to.
:param int address: The I2C device address. Defaults to :const:`0x30`
:raises RuntimeError: if the sensor is not found
**Quickstart: Importing and using the device**
Here is an example of using the :class:`MMC5603` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
from machine import Pin, I2C
from micropython_mmc5603 import mmc5603
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))
mmc = mmc5603.MMC5603(i2c)
Now you have access to the attributes
.. code-block:: python
magx, magy, magz = mmc.magnetic
"""
_device_id = RegisterStruct(_REG_WHOIAM, "<B")
_ctrl0_reg = RegisterStruct(_CTRL_REG0, "<B")
_ctrl1_reg = RegisterStruct(_CTRL_REG1, "<B")
_ctrl2_reg = RegisterStruct(_CTRL_REG2, "B")
_odr_reg = RegisterStruct(_ODR_REG, "<B")
_raw_temp_data = RegisterStruct(_TEMP, "B")
_meas_m_done = CBits(1, _STATUS_REG, 6)
_meas_t_done = CBits(1, _STATUS_REG, 7)
def __init__(self, i2c, address: int = 0x30) -> None:
self._i2c = i2c
self._address = address
if self._device_id != 0x10:
raise RuntimeError("Failed to find MMC5603")
self._ctrl2_cache = 0
self._odr_cache = 0
self._measure_time_cached = 0
self._buffer = bytearray(9)
# self.continuous_mode = False
self._ctrl1_reg = 0x80
time.sleep(0.020)
self._ctrl0_reg = 0x08 # Do_Set
time.sleep(0.001)
self._ctrl0_reg = 0x10 # Do_Reset
time.sleep(0.001)
self._ctrl0_reg = 0x20 # Set the Do_Set-Do_Reset Automatically
time.sleep(0.001)
@property
def magnetic(self) -> Tuple[float, float, float]:
"""The processed magnetometer sensor values.
A 3-tuple of X, Y, Z axis values in microteslas that are signed floats.
"""
if not self.continuous_mode:
self._ctrl0_reg = 0x01
while not self._meas_m_done:
time.sleep(0.005)
self._i2c.readfrom_mem_into(self._address, _DATA, self._buffer)
x = self._buffer[0] << 12 | self._buffer[1] << 4 | self._buffer[6] >> 4
y = self._buffer[2] << 12 | self._buffer[3] << 4 | self._buffer[7] >> 4
z = self._buffer[4] << 12 | self._buffer[5] << 4 | self._buffer[8] >> 4
x -= 1 << 19
y -= 1 << 19
z -= 1 << 19
# scale to uT by LSB in datasheet
x *= 0.00625
y *= 0.00625
z *= 0.00625
return x, y, z
@property
def temperature(self) -> float:
"""The processed temperature sensor value, returned in floating point C"""
if self.continuous_mode:
raise RuntimeError("Can only read temperature when not in continuous mode")
self._ctrl0_reg = 0x02
while not self._meas_t_done:
time.sleep(0.005)
temp = self._raw_temp_data
temp *= 0.8
temp -= 75
return temp
@property
def data_rate(self) -> int:
"""Output data rate, 0 for on-request data.
1-255 or 1000 for freq of continuous-mode readings"""
return self._odr_cache
@data_rate.setter
def data_rate(self, value: int) -> None:
if not ((value == 1000) or (0 <= value <= 255)):
raise ValueError("Data rate must be 0-255 or 1000 Hz")
self._odr_cache = value
if value == 1000:
self._odr_reg = 255
self._ctrl2_cache |= 0x80 # turn on hpower bit
self._ctrl2_reg = self._ctrl2_cache
else:
self._odr_reg = value
self._ctrl2_cache &= ~0x80 # turn off hpower bit
self._ctrl2_reg = self._ctrl2_cache
@property
def continuous_mode(self) -> bool:
"""Whether or not to put the chip in continous mode - be sure
to set the data_rate as well!
"""
return self._ctrl2_cache & 0x10
@continuous_mode.setter
def continuous_mode(self, value: bool) -> None:
if value:
self._ctrl0_reg = 0x80 # turn on cmm_freq_en bit
self._ctrl2_cache |= 0x10 # turn on cmm_en bit
else:
self._ctrl2_cache &= ~0x10 # turn off cmm_en bit
self._ctrl2_reg = self._ctrl2_cache
@property
def measure_time(self) -> str:
"""
Sensor measure_time adjust the length of the decimation filter.
They control the duration of each measurement.
Note: X/Y/Z channel measurements are taken sequentially. Delay Time
among those measurements is 1/3 of the Measurement
+------------------------------+------------------+
| Mode | Value |
+==============================+==================+
| :py:const:`mmc5603.MT_6_6ms` | :py:const:`0b00` |
+------------------------------+------------------+
| :py:const:`mmc5603.MT_3_5ms` | :py:const:`0b01` |
+------------------------------+------------------+
| :py:const:`mmc5603.MT_2_0ms` | :py:const:`0b10` |
+------------------------------+------------------+
| :py:const:`mmc5603.MT_1_2ms` | :py:const:`0b11` |
+------------------------------+------------------+
"""
values = ("MT_6_6ms", "MT_3_5ms", "MT_2_0ms", "MT_1_2ms")
return values[self._measure_time_cached]
@measure_time.setter
def measure_time(self, value: int) -> None:
if value not in measure_time_values:
raise ValueError("Value must be a valid measure_time setting")
self._ctrl1_reg = value
self._measure_time_cached = value