|
1 | | -# LIS2MDL Magnetometer Driver |
| 1 | +# LIS2MDL Magnetometer Driver for MicroPython |
2 | 2 |
|
3 | | -This is a MicroPython driver for the LIS2MDL 3-axis magnetometer. The LIS2MDL is a high-performance magnetic sensor designed for applications such as electronic compasses, motion tracking, and orientation detection. This driver provides methods for initialization, calibration, and reading magnetic field data. |
| 3 | +A complete and feature-rich driver for the **LIS2MDL 3-axis magnetometer** by STMicroelectronics. |
| 4 | +This library allows easy integration of the LIS2MDL sensor with MicroPython or CircuitPython boards using I²C. |
| 5 | +It provides low-level register access, automatic calibration, heading computation, and tilt compensation — ideal for **digital compasses**, **robot navigation**, and **orientation tracking**. |
4 | 6 |
|
5 | | -## Features |
6 | | -- Soft reset and initialization of the LIS2MDL sensor. |
7 | | -- Configurable output data rate (ODR) and low-power mode. |
8 | | -- 3D calibration for offsets and scales. |
9 | | -- Heading calculation with and without tilt compensation. |
10 | | -- Direction labeling (e.g., N, NE, E, etc.). |
| 7 | +--- |
11 | 8 |
|
12 | | -## Requirements |
13 | | -- MicroPython-compatible board with I2C support. |
14 | | -- LIS2MDL sensor module. |
| 9 | +## 🧭 Features |
15 | 10 |
|
16 | | -## Installation |
17 | | -1. Copy the `lis2mdl` folder to your MicroPython device. |
18 | | -2. Import the driver in your script: |
19 | | - ```python |
20 | | - from lis2mdl.device import LIS2MDL |
21 | | - ``` |
| 11 | +* ✅ Full **I²C driver** for LIS2MDL |
| 12 | +* ✅ Supports **10 / 20 / 50 / 100 Hz** output data rates |
| 13 | +* ✅ **Temperature-compensated** and **low-power** modes |
| 14 | +* ✅ Read **raw**, **scaled**, or **calibrated** magnetic field data |
| 15 | +* ✅ **2D and 3D calibration** routines (auto min/max method) |
| 16 | +* ✅ **Heading computation** (flat or tilt-compensated) |
| 17 | +* ✅ **Compass direction labels** (N, NE, E, …) |
| 18 | +* ✅ Built-in **digital low-pass filter** and **offset cancellation** control |
| 19 | +* ✅ Diagnostic utilities: |
| 20 | + |
| 21 | + * Register dump |
| 22 | + * Calibration quality metrics |
| 23 | + * Self-test and temperature readout |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## ⚙️ Hardware Requirements |
| 28 | + |
| 29 | +* **LIS2MDL** 3-axis magnetometer (STMicroelectronics) |
| 30 | +* **MicroPython/CircuitPython board**, e.g.: |
| 31 | + |
| 32 | + * ESP32 / ESP8266 |
| 33 | + * Raspberry Pi Pico / RP2040 |
| 34 | + * STM32 Nucleo or Pyboard |
| 35 | +* 3.3V power supply |
| 36 | +* I²C interface (SCL, SDA) |
| 37 | + |
| 38 | +--- |
| 39 | + |
| 40 | +## 🔌 Wiring Example (ESP32) |
| 41 | + |
| 42 | +| LIS2MDL Pin | ESP32 Pin | Description | |
| 43 | +| ----------- | --------- | ------------ | |
| 44 | +| VIN | 3.3V | Power supply | |
| 45 | +| GND | GND | Ground | |
| 46 | +| SDA | GPIO21 | I²C Data | |
| 47 | +| SCL | GPIO22 | I²C Clock | |
| 48 | + |
| 49 | +Example setup: |
22 | 50 |
|
23 | | -## Usage |
24 | | -### Basic Example |
25 | 51 | ```python |
26 | | -from time import sleep_ms |
27 | | -from machine import I2C |
28 | | -from lis2mdl.device import LIS2MDL |
| 52 | +from machine import I2C, Pin |
| 53 | +from device import LIS2MDL |
29 | 54 |
|
30 | | -# Initialize I2C and LIS2MDL |
31 | | -i2c = I2C(1) |
| 55 | +i2c = I2C(1, scl=Pin(22), sda=Pin(21)) |
32 | 56 | mag = LIS2MDL(i2c) |
| 57 | +``` |
33 | 58 |
|
34 | | -# Perform calibration |
35 | | -mag.calibrate_step() |
36 | | -calibration_values = mag.get_calibration() |
37 | | -print("Calibration values:", calibration_values) |
| 59 | +--- |
38 | 60 |
|
39 | | -# Continuous heading reading |
40 | | -print("Continuous heading readings:") |
41 | | -while True: |
42 | | - angle = mag.heading_flat_only() |
43 | | - direction = mag.direction_label(angle) |
44 | | - print(f"{direction} | angle={angle:.2f}°") |
45 | | - sleep_ms(100) |
| 61 | +## 🧩 Installation |
| 62 | + |
| 63 | +### 1. Copy files to your board |
| 64 | + |
| 65 | +Upload both files using Thonny, mpremote, or ampy: |
| 66 | + |
| 67 | +``` |
| 68 | +device.py |
| 69 | +lis2mdl/const.py |
46 | 70 | ``` |
47 | 71 |
|
48 | | -### Tilt Compensation Example |
49 | | -To use tilt compensation, you need an accelerometer to provide roll and pitch data. Pass a function that reads accelerometer data to the `heading_with_tilt_compensation` method. |
| 72 | +### 2. Import and initialize |
| 73 | + |
| 74 | +```python |
| 75 | +from machine import I2C, Pin |
| 76 | +from device import LIS2MDL |
| 77 | + |
| 78 | +i2c = I2C(1, scl=Pin(22), sda=Pin(21)) |
| 79 | +mag = LIS2MDL(i2c) |
| 80 | +``` |
| 81 | + |
| 82 | +--- |
| 83 | + |
| 84 | +## 🚀 Quick Start |
| 85 | + |
| 86 | +### Read magnetic field |
| 87 | + |
| 88 | +```python |
| 89 | +x, y, z = mag.read_magnet_uT() |
| 90 | +print("Magnetic field (µT):", x, y, z) |
| 91 | +``` |
| 92 | + |
| 93 | +### Get heading (flat orientation) |
| 94 | + |
| 95 | +```python |
| 96 | +heading = mag.heading_flat_only() |
| 97 | +print("Heading: {:.1f}° {}".format(heading, mag.direction_label(heading))) |
| 98 | +``` |
| 99 | + |
| 100 | +### With tilt compensation |
| 101 | + |
| 102 | +Requires a function returning accelerometer data `(ax, ay, az)` in g: |
50 | 103 |
|
51 | 104 | ```python |
52 | 105 | def read_accel(): |
53 | | - # Replace with your accelerometer reading logic |
54 | | - return ax, ay, az |
| 106 | + return (0.0, 0.0, 1.0) # Example static vector |
| 107 | + |
| 108 | +heading = mag.heading_with_tilt_compensation(read_accel) |
| 109 | +``` |
| 110 | + |
| 111 | +--- |
| 112 | + |
| 113 | +## ⚖️ Calibration |
| 114 | + |
| 115 | +Calibration is essential to obtain accurate compass readings. |
| 116 | +The driver provides **automated routines** for both 2D (flat) and 3D calibration. |
| 117 | + |
| 118 | +### 2D Calibration (Flat) |
| 119 | + |
| 120 | +Rotate the board **horizontally** in all directions: |
| 121 | + |
| 122 | +```python |
| 123 | +mag.calibrate_minmax_2d(samples=300) |
| 124 | +``` |
| 125 | + |
| 126 | +### 3D Calibration (Full) |
55 | 127 |
|
56 | | -angle = mag.heading_with_tilt_compensation(read_accel) |
57 | | -print(f"Tilt-compensated angle: {angle:.2f}°") |
| 128 | +Rotate the board **in all orientations**: |
| 129 | + |
| 130 | +```python |
| 131 | +mag.calibrate_minmax_3d(samples=600) |
| 132 | +``` |
| 133 | + |
| 134 | +### Apply and test calibration |
| 135 | + |
| 136 | +```python |
| 137 | +print("Calibration:", mag.read_calibration()) |
| 138 | +quality = mag.calibrate_quality() |
| 139 | +print("Quality metrics:", quality) |
| 140 | +``` |
| 141 | + |
| 142 | +### Reset calibration |
| 143 | + |
| 144 | +```python |
| 145 | +mag.calibrate_reset() |
58 | 146 | ``` |
59 | 147 |
|
60 | | -## Calibration |
61 | | -The `calibrate_step` method performs a quick 3D calibration. Rotate the sensor flat over 360° to capture the minimum and maximum magnetic field values. The offsets and scales are calculated automatically. |
| 148 | +--- |
| 149 | + |
| 150 | +## 🧰 Configuration Examples |
| 151 | + |
| 152 | +### Output rate and power |
| 153 | + |
| 154 | +```python |
| 155 | +mag.set_odr(50) # 50 Hz |
| 156 | +mag.set_low_power(True) # Enable low-power mode |
| 157 | +``` |
| 158 | + |
| 159 | +### Enable digital low-pass filter |
| 160 | + |
| 161 | +```python |
| 162 | +mag.set_low_pass(True) |
| 163 | +``` |
| 164 | + |
| 165 | +### Configure declination and heading offset |
| 166 | + |
| 167 | +```python |
| 168 | +mag.set_declination(1.5) # Magnetic declination (°) |
| 169 | +mag.set_heading_offset(-5.0) # Align physical 0° |
| 170 | +``` |
| 171 | + |
| 172 | +### Block data update (BDU) |
| 173 | + |
| 174 | +```python |
| 175 | +mag.set_bdu(True) |
| 176 | +``` |
| 177 | + |
| 178 | +--- |
| 179 | + |
| 180 | +## 🧮 Heading Filtering |
| 181 | + |
| 182 | +You can apply a **smoothing filter** on the heading angle to stabilize the readings: |
| 183 | + |
| 184 | +```python |
| 185 | +mag.set_heading_filter(alpha=0.2) # Light smoothing |
| 186 | +``` |
| 187 | + |
| 188 | +* `alpha = 0.0` → no filter |
| 189 | +* `alpha = 0.1–0.3` → medium smoothing |
| 190 | +* `alpha = 1.0` → very heavy smoothing |
| 191 | + |
| 192 | +--- |
| 193 | + |
| 194 | +## 🔍 Diagnostics & Debug |
| 195 | + |
| 196 | +### Read sensor ID |
| 197 | + |
| 198 | +```python |
| 199 | +print("WHO_AM_I:", hex(mag.read_who_am_i())) |
| 200 | +``` |
| 201 | + |
| 202 | +Expected value: `0x40` |
| 203 | + |
| 204 | +### Read temperature (approximate) |
| 205 | + |
| 206 | +```python |
| 207 | +print("Temperature (°C):", mag.read_temperature_c()) |
| 208 | +``` |
| 209 | + |
| 210 | +### Check data readiness |
| 211 | + |
| 212 | +```python |
| 213 | +if mag.data_ready(): |
| 214 | + print("New magnetic data available!") |
| 215 | +``` |
| 216 | + |
| 217 | +### Dump consecutive registers |
| 218 | + |
| 219 | +```python |
| 220 | +regs = mag.read_registers(0x60, 12) |
| 221 | +print("Register dump:", regs) |
| 222 | +``` |
| 223 | + |
| 224 | +--- |
| 225 | + |
| 226 | +## 🧠 Internal Methods Overview |
| 227 | + |
| 228 | +| Method | Description | |
| 229 | +| ---------------------------------- | ------------------------------ | |
| 230 | +| `read_magnet_raw()` | Raw sensor values (int16) | |
| 231 | +| `read_magnet_uT()` | Magnetic field in µT | |
| 232 | +| `read_magnet_calibrated_norm()` | Calibrated and normalized data | |
| 233 | +| `heading_flat_only()` | Flat compass heading | |
| 234 | +| `heading_with_tilt_compensation()` | Tilt-corrected heading | |
| 235 | +| `read_temperature_c()` | Read relative temperature | |
| 236 | +| `power_down()` / `wake()` | Power management | |
| 237 | +| `soft_reset()` / `reboot()` | Sensor reset functions | |
| 238 | + |
| 239 | +--- |
| 240 | + |
| 241 | +## 🧪 Example: Continuous Compass Loop |
| 242 | + |
| 243 | +```python |
| 244 | +from machine import I2C, Pin |
| 245 | +from device import LIS2MDL |
| 246 | +import time |
| 247 | + |
| 248 | +i2c = I2C(1, scl=Pin(22), sda=Pin(21)) |
| 249 | +mag = LIS2MDL(i2c) |
| 250 | +mag.set_declination(2.3) |
| 251 | + |
| 252 | +while True: |
| 253 | + heading = mag.heading_flat_only() |
| 254 | + print("Heading: {:.1f}° {}".format(heading, mag.direction_label(heading))) |
| 255 | + time.sleep(0.5) |
| 256 | +``` |
| 257 | + |
| 258 | +--- |
| 259 | + |
| 260 | +## 🧾 Notes |
| 261 | + |
| 262 | +* The LIS2MDL outputs magnetic data in **LSB**, with a conversion factor of **0.15 µT/LSB**. |
| 263 | +* Temperature readings are **relative only** (not absolute). |
| 264 | +* Calibration should be repeated if the sensor environment changes (metal nearby, relocation, etc.). |
| 265 | +* Tilt compensation requires an external **accelerometer** or IMU (e.g., LIS3DH, MPU6050, BNO055). |
| 266 | + |
| 267 | +--- |
| 268 | + |
| 269 | +## 📚 References |
62 | 270 |
|
63 | | -## Datasheet |
64 | | -For detailed information about the LIS2MDL sensor, refer to the [datasheet](https://www.st.com/resource/en/datasheet/lis2mdl.pdf). |
| 271 | +* [STMicroelectronics LIS2MDL Datasheet](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) |
| 272 | +* [MicroPython Documentation](https://docs.micropython.org/en/latest/library/machine.I2C.html) |
65 | 273 |
|
66 | | -## Notes |
67 | | -- The `heading_with_tilt_compensation` method has not been tested due to the lack of an accelerometer during development. |
68 | | -- Ensure the I2C pins are correctly connected to the LIS2MDL module. |
| 274 | +--- |
0 commit comments