├── .arduino-ci.yaml ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report-or-feature-request.md │ └── config.yml └── workflows │ └── ci.yaml ├── .gitignore ├── .gitlab-ci.yml ├── LICENSE.txt ├── LIS3MDL.cpp ├── LIS3MDL.h ├── README.md ├── examples ├── Calibrate │ └── Calibrate.ino ├── HeadingWithLSM6 │ └── HeadingWithLSM6.ino └── Serial │ └── Serial.ino ├── keywords.txt └── library.properties /.arduino-ci.yaml: -------------------------------------------------------------------------------- 1 | library_archives: 2 | - LSM6=https://github.com/pololu/lsm6-arduino/archive/refs/tags/2.0.1.tar.gz=1q0vpxwnbig01nipw3vi2k3q5yf900n8ar26qgni7fyfpb4s9xx5 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report-or-feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report or feature request 3 | about: Did you find a specific bug in the code for this project? Do you want to request 4 | a new feature? Please open an issue! 5 | title: '' 6 | labels: '' 7 | assignees: '' 8 | 9 | --- 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Pololu Forum 4 | url: https://forum.pololu.com/ 5 | about: Do you need help getting started? Can't get this code to work at all? Having problems with electronics? Please post on our forum! 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: "CI" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-20.04 8 | steps: 9 | - name: Checkout this repository 10 | uses: actions/checkout@v2.3.4 11 | - name: Cache for arduino-ci 12 | uses: actions/cache@v2.1.3 13 | with: 14 | path: | 15 | ~/.arduino15 16 | key: ${{ runner.os }}-arduino 17 | - name: Install nix 18 | uses: cachix/install-nix-action@v12 19 | - run: nix-shell -I nixpkgs=channel:nixpkgs-unstable -p arduino-ci --run "arduino-ci" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /docs/ 2 | /out/ 3 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: $CI_REGISTRY_IMAGE/nixos/nix:2.3.6 2 | 3 | stages: 4 | - ci 5 | 6 | ci: 7 | stage: ci 8 | tags: 9 | - nix 10 | script: 11 | - nix-shell -I nixpkgs=channel:nixpkgs-unstable -p arduino-ci --run "arduino-ci" 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2023 Pololu Corporation (www.pololu.com) 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /LIS3MDL.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Defines //////////////////////////////////////////////////////////////// 6 | 7 | // The Arduino two-wire interface uses a 7-bit number for the address, 8 | // and sets the last bit correctly based on reads and writes 9 | #define LIS3MDL_SA1_HIGH_ADDRESS 0b0011110 10 | #define LIS3MDL_SA1_LOW_ADDRESS 0b0011100 11 | 12 | #define TEST_REG_ERROR -1 13 | 14 | #define LIS3MDL_WHO_ID 0x3D 15 | 16 | // Constructors //////////////////////////////////////////////////////////////// 17 | 18 | LIS3MDL::LIS3MDL(void) 19 | { 20 | _device = device_auto; 21 | } 22 | 23 | // Public Methods ////////////////////////////////////////////////////////////// 24 | 25 | bool LIS3MDL::init(deviceType device, sa1State sa1) 26 | { 27 | // perform auto-detection unless device type and SA1 state were both specified 28 | if (device == device_auto || sa1 == sa1_auto) 29 | { 30 | // check for LIS3MDL if device is unidentified or was specified to be this type 31 | if (device == device_auto || device == device_LIS3MDL) 32 | { 33 | // check SA1 high address unless SA1 was specified to be low 34 | if (sa1 != sa1_low && testReg(LIS3MDL_SA1_HIGH_ADDRESS, WHO_AM_I) == LIS3MDL_WHO_ID) 35 | { 36 | sa1 = sa1_high; 37 | if (device == device_auto) { device = device_LIS3MDL; } 38 | } 39 | // check SA1 low address unless SA1 was specified to be high 40 | else if (sa1 != sa1_high && testReg(LIS3MDL_SA1_LOW_ADDRESS, WHO_AM_I) == LIS3MDL_WHO_ID) 41 | { 42 | sa1 = sa1_low; 43 | if (device == device_auto) { device = device_LIS3MDL; } 44 | } 45 | } 46 | 47 | // make sure device and SA1 were successfully detected; otherwise, indicate failure 48 | if (device == device_auto || sa1 == sa1_auto) 49 | { 50 | return false; 51 | } 52 | } 53 | 54 | _device = device; 55 | 56 | switch (device) 57 | { 58 | case device_LIS3MDL: 59 | address = (sa1 == sa1_high) ? LIS3MDL_SA1_HIGH_ADDRESS : LIS3MDL_SA1_LOW_ADDRESS; 60 | break; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | /* 67 | Enables the LIS3MDL's magnetometer. Also: 68 | - Selects ultra-high-performance mode for all axes 69 | - Sets ODR (output data rate) to default power-on value of 10 Hz 70 | - Sets magnetometer full scale (gain) to default power-on value of +/- 4 gauss 71 | - Enables continuous conversion mode 72 | Note that this function will also reset other settings controlled by 73 | the registers it writes to. 74 | */ 75 | void LIS3MDL::enableDefault(void) 76 | { 77 | if (_device == device_LIS3MDL) 78 | { 79 | // 0x70 = 0b01110000 80 | // OM = 11 (ultra-high-performance mode for X and Y); DO = 100 (10 Hz ODR) 81 | writeReg(CTRL_REG1, 0x70); 82 | 83 | // 0x00 = 0b00000000 84 | // FS = 00 (+/- 4 gauss full scale) 85 | writeReg(CTRL_REG2, 0x00); 86 | 87 | // 0x00 = 0b00000000 88 | // MD = 00 (continuous-conversion mode) 89 | writeReg(CTRL_REG3, 0x00); 90 | 91 | // 0x0C = 0b00001100 92 | // OMZ = 11 (ultra-high-performance mode for Z) 93 | writeReg(CTRL_REG4, 0x0C); 94 | 95 | // 0x40 = 0b01000000 96 | // BDU = 1 (block data update) 97 | writeReg(CTRL_REG5, 0x40); 98 | } 99 | } 100 | 101 | // Writes a mag register 102 | void LIS3MDL::writeReg(uint8_t reg, uint8_t value) 103 | { 104 | Wire.beginTransmission(address); 105 | Wire.write(reg); 106 | Wire.write(value); 107 | last_status = Wire.endTransmission(); 108 | } 109 | 110 | // Reads a mag register 111 | uint8_t LIS3MDL::readReg(uint8_t reg) 112 | { 113 | uint8_t value; 114 | 115 | Wire.beginTransmission(address); 116 | Wire.write(reg); 117 | last_status = Wire.endTransmission(); 118 | 119 | Wire.requestFrom(address, (uint8_t)1); 120 | value = Wire.read(); 121 | 122 | return value; 123 | } 124 | 125 | // Reads the 3 mag channels and stores them in vector m 126 | void LIS3MDL::read() 127 | { 128 | Wire.beginTransmission(address); 129 | // assert MSB to enable subaddress updating 130 | Wire.write(OUT_X_L | 0x80); 131 | Wire.endTransmission(); 132 | 133 | Wire.requestFrom(address, (uint8_t)6); 134 | uint8_t xlm = Wire.read(); 135 | uint8_t xhm = Wire.read(); 136 | uint8_t ylm = Wire.read(); 137 | uint8_t yhm = Wire.read(); 138 | uint8_t zlm = Wire.read(); 139 | uint8_t zhm = Wire.read(); 140 | 141 | // combine high and low bytes 142 | m.x = (int16_t)(xhm << 8 | xlm); 143 | m.y = (int16_t)(yhm << 8 | ylm); 144 | m.z = (int16_t)(zhm << 8 | zlm); 145 | } 146 | 147 | void LIS3MDL::vector_normalize(vector *a) 148 | { 149 | float mag = sqrt(vector_dot(a, a)); 150 | a->x /= mag; 151 | a->y /= mag; 152 | a->z /= mag; 153 | } 154 | 155 | // Private Methods ////////////////////////////////////////////////////////////// 156 | 157 | int16_t LIS3MDL::testReg(uint8_t address, regAddr reg) 158 | { 159 | Wire.beginTransmission(address); 160 | Wire.write((uint8_t)reg); 161 | if (Wire.endTransmission() != 0) 162 | { 163 | return TEST_REG_ERROR; 164 | } 165 | 166 | Wire.requestFrom(address, (uint8_t)1); 167 | if (Wire.available()) 168 | { 169 | return Wire.read(); 170 | } 171 | else 172 | { 173 | return TEST_REG_ERROR; 174 | } 175 | } -------------------------------------------------------------------------------- /LIS3MDL.h: -------------------------------------------------------------------------------- 1 | #ifndef LIS3MDL_h 2 | #define LIS3MDL_h 3 | 4 | #include 5 | 6 | class LIS3MDL 7 | { 8 | public: 9 | template struct vector 10 | { 11 | T x, y, z; 12 | }; 13 | 14 | enum deviceType { device_LIS3MDL, device_auto }; 15 | enum sa1State { sa1_low, sa1_high, sa1_auto }; 16 | 17 | // register addresses 18 | enum regAddr 19 | { 20 | WHO_AM_I = 0x0F, 21 | 22 | CTRL_REG1 = 0x20, 23 | CTRL_REG2 = 0x21, 24 | CTRL_REG3 = 0x22, 25 | CTRL_REG4 = 0x23, 26 | CTRL_REG5 = 0x24, 27 | 28 | STATUS_REG = 0x27, 29 | OUT_X_L = 0x28, 30 | OUT_X_H = 0x29, 31 | OUT_Y_L = 0x2A, 32 | OUT_Y_H = 0x2B, 33 | OUT_Z_L = 0x2C, 34 | OUT_Z_H = 0x2D, 35 | TEMP_OUT_L = 0x2E, 36 | TEMP_OUT_H = 0x2F, 37 | INT_CFG = 0x30, 38 | INT_SRC = 0x31, 39 | INT_THS_L = 0x32, 40 | INT_THS_H = 0x33, 41 | }; 42 | 43 | vector m; // magnetometer readings 44 | 45 | uint8_t last_status; // status of last I2C transmission 46 | 47 | LIS3MDL(void); 48 | 49 | bool init(deviceType device = device_auto, sa1State sa1 = sa1_auto); 50 | deviceType getDeviceType(void) { return _device; } 51 | 52 | void enableDefault(void); 53 | 54 | void writeReg(uint8_t reg, uint8_t value); 55 | uint8_t readReg(uint8_t reg); 56 | 57 | void read(void); 58 | 59 | // vector functions 60 | template static void vector_cross(const vector *a, const vector *b, vector *out); 61 | template static float vector_dot(const vector *a, const vector *b); 62 | static void vector_normalize(vector *a); 63 | 64 | private: 65 | deviceType _device; // chip type 66 | uint8_t address; 67 | 68 | int16_t testReg(uint8_t address, regAddr reg); 69 | }; 70 | 71 | template void LIS3MDL::vector_cross(const vector *a, const vector *b, vector *out) 72 | { 73 | out->x = (a->y * b->z) - (a->z * b->y); 74 | out->y = (a->z * b->x) - (a->x * b->z); 75 | out->z = (a->x * b->y) - (a->y * b->x); 76 | } 77 | 78 | template float LIS3MDL::vector_dot(const vector *a, const vector *b) 79 | { 80 | return (a->x * b->x) + (a->y * b->y) + (a->z * b->z); 81 | } 82 | 83 | #endif 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LIS3MDL library for Arduino 2 | 3 | [www.pololu.com](https://www.pololu.com/) 4 | 5 | ## Summary 6 | 7 | This is a library for the Arduino IDE that helps interface with ST's LIS3MDL 3-axis magnetometer via I²C. It makes it simple to configure the LIS3MDL and read the raw magnetometer data from these boards: 8 | 9 | * [LIS3MDL 3-axis magnetometer carrier](https://www.pololu.com/product/2737) 10 | * [MinIMU-9 v6 (LSM6DSO and LIS3MDL carrier)](https://www.pololu.com/product/2862) 11 | * [AltIMU-10 v6 (LSM6DSO, LIS3MDL, and LPS22DF Carrier)](https://www.pololu.com/product/2863) 12 | * [MinIMU-9 v5 (LSM6DS33 and LIS3MDL carrier)](https://www.pololu.com/product/2738) 13 | * [AltIMU-9 v5 (LSM6DS33, LIS3MDL, and LPS25H carrier)](https://www.pololu.com/product/2739) 14 | 15 | ## Supported platforms 16 | 17 | This library is designed to work with the Arduino IDE versions 1.6.x or later; we have not tested it with earlier versions. This library should support any Arduino-compatible board, including the [Pololu A-Star 32U4 controllers](https://www.pololu.com/category/149/a-star-programmable-controllers). 18 | 19 | ## Getting started 20 | 21 | ### Hardware 22 | 23 | An LIS3MDL carrier can be purchased from Pololu's website. Before continuing, careful reading of the product page as well as the LIS3MDL datasheet and application note is recommended. 24 | 25 | Make the following connections between the Arduino and the LIS3MDL board: 26 | 27 | #### 5V Arduino boards 28 | 29 | (including Arduino Uno, Leonardo, Mega; Pololu A-Star 32U4) 30 | 31 | Arduino LIS3MDL board 32 | ------- ------------- 33 | 5V - VIN 34 | GND - GND 35 | SDA - SDA 36 | SCL - SCL 37 | 38 | #### 3.3V Arduino boards 39 | 40 | (including Arduino Due) 41 | 42 | Arduino LIS3MDL board 43 | ------- ------------- 44 | 3V3 - VIN 45 | GND - GND 46 | SDA - SDA 47 | SCL - SCL 48 | 49 | ### Software 50 | 51 | If you are using version 1.6.2 or later of the [Arduino software (IDE)](http://www.arduino.cc/en/Main/Software), you can use the Library Manager to install this library: 52 | 53 | 1. In the Arduino IDE, open the "Sketch" menu, select "Include Library", then "Manage Libraries...". 54 | 2. Search for "LIS3MDL". 55 | 3. Click the LIS3MDL entry in the list. 56 | 4. Click "Install". 57 | 58 | If this does not work, you can manually install the library: 59 | 60 | 1. Download the [latest release archive from GitHub](https://github.com/pololu/lis3mdl-arduino/releases) and decompress it. 61 | 2. Rename the folder "lis3mdl-arduino-master" to "LIS3MDL". 62 | 3. Move the "LIS3MDL" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself. 63 | 4. After installing the library, restart the Arduino IDE. 64 | 65 | ## Examples 66 | 67 | Several example sketches are available that show how to use the library. You can access them from the Arduino IDE by opening the "File" menu, selecting "Examples", and then selecting "LIS3MDL". If you cannot find these examples, the library was probably installed incorrectly and you should retry the installation instructions above. 68 | 69 | ## Library reference 70 | 71 | * `vector m`
72 | The last values read from the magnetometer. 73 | 74 | * `uint8_t last_status`
75 | The status of the last I²C write transmission. See the [`Wire.endTransmission()` documentation](http://arduino.cc/en/Reference/WireEndTransmission) for return values. 76 | 77 | * `LIS3MDL(void)`
78 | Constructor. 79 | 80 | * `bool init(deviceType device, sa0State sa0)`
81 | Initializes the library with the device being used (`device_LIS3MDL` or `device_auto`) and the state of the SA1 pin (`sa1_low`, `sa1_high`, or `sa1_auto`), which determines the second-least significant bit of the I²C slave address. Constants for these arguments are defined in LIS3MDL.h. Both of these arguments are optional; if they are not specified, the library will try to automatically detect the device address. A boolean is returned indicating whether the type of device was successfully determined (if necessary). 82 | 83 | * `void getDeviceType(void)`
84 | Returns the device type specified to or detected by `init()`. 85 | 86 | * `void enableDefault(void)`
87 | Turns on the magnetometer and enables a consistent set of default settings. 88 | 89 | This function will reset the magnetometer to ±4 gauss full scale. See the comments in LIS3MDL.cpp for a full explanation of the settings. 90 | 91 | * `void writeReg(uint8_t reg, uint8_t value)`
92 | Writes a sensor register with the given value. 93 | 94 | Register address constants are defined by the regAddr enumeration type in LIS3MDL.h.
95 | Example use: `mag.writeReg(LIS3MDL::CTRL_REG1, 0x70);` 96 | 97 | * `uint8_t readReg(uint8_t reg)`
98 | Reads a sensor register and returns the value read. 99 | 100 | * `void read(void)`
101 | Takes a reading from the magnetometer and stores the values in the vector `m`. Conversion of the readings to units of gauss depends on the magnetometer's selected gain (full scale setting). 102 | 103 | * `void setTimeout(uint16_t timeout)`
104 | Sets a timeout period in milliseconds after which the read functions will abort if the sensor is not ready. A value of 0 disables the timeout. 105 | 106 | * `uint16_t getTimeout(void)`
107 | Returns the current timeout period setting. 108 | 109 | * `bool timeoutOccurred(void)`
110 | Indicates whether a read timeout has occurred since the last call to `timeoutOccurred()`. 111 | 112 | ## Version history 113 | 114 | * 2.0.0 (2023-06-13): Removed timeout functionality that did not work as intended. Changed `enableDefault()` to enable BDU. Added HeadingWithLSM6 example. 115 | * 1.0.0 (2016-01-19): Original release. 116 | -------------------------------------------------------------------------------- /examples/Calibrate/Calibrate.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | LIS3MDL mag; 5 | LIS3MDL::vector running_min = {32767, 32767, 32767}, running_max = {-32768, -32768, -32768}; 6 | 7 | char report[80]; 8 | 9 | void setup() 10 | { 11 | Serial.begin(9600); 12 | Wire.begin(); 13 | 14 | if (!mag.init()) 15 | { 16 | Serial.println("Failed to detect and initialize magnetometer!"); 17 | while (1); 18 | } 19 | 20 | mag.enableDefault(); 21 | } 22 | 23 | void loop() 24 | { 25 | mag.read(); 26 | 27 | running_min.x = min(running_min.x, mag.m.x); 28 | running_min.y = min(running_min.y, mag.m.y); 29 | running_min.z = min(running_min.z, mag.m.z); 30 | 31 | running_max.x = max(running_max.x, mag.m.x); 32 | running_max.y = max(running_max.y, mag.m.y); 33 | running_max.z = max(running_max.z, mag.m.z); 34 | 35 | snprintf(report, sizeof(report), "min: {%+6d, %+6d, %+6d} max: {%+6d, %+6d, %+6d}", 36 | running_min.x, running_min.y, running_min.z, 37 | running_max.x, running_max.y, running_max.z); 38 | Serial.println(report); 39 | 40 | delay(100); 41 | } 42 | -------------------------------------------------------------------------------- /examples/HeadingWithLSM6/HeadingWithLSM6.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch combines magnetometer readings from an LIS3MDL and accelerometer 3 | readings from an LSM6 to calculate a tilt-compensated magnetic heading. It 4 | requires Pololu's LSM6 Arduino library to be installed: 5 | 6 | https://github.com/pololu/lsm6-arduino 7 | 8 | This program can be used with a board that includes both sensors, like the 9 | Pololu MinIMU-9 v5 and AltIMU-10 v5, or with separate carrier boards for the two 10 | sensors, both connected to the same I2C bus. If you are using separate boards, 11 | make sure the axes are oriented the same way on both (i.e. the X, Y, and Z axes 12 | of both boards should point in the same direction, and the surfaces of the 13 | boards should be as close to parallel as possible). 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | LIS3MDL mag; 21 | LSM6 imu; 22 | 23 | /* 24 | Calibration values; the default values of +/-32767 for each axis 25 | lead to an assumed magnetometer bias of 0. Use the Calibrate example 26 | program to determine appropriate values for your particular unit. 27 | */ 28 | LIS3MDL::vector m_min = {-32767, -32767, -32767}; 29 | LIS3MDL::vector m_max = {+32767, +32767, +32767}; 30 | 31 | void setup() 32 | { 33 | Serial.begin(9600); 34 | Wire.begin(); 35 | 36 | if (!mag.init()) 37 | { 38 | Serial.println("Failed to detect and initialize LIS3MDL magnetometer!"); 39 | while (1); 40 | } 41 | mag.enableDefault(); 42 | 43 | if (!imu.init()) 44 | { 45 | Serial.println("Failed to detect and initialize LSM6 IMU!"); 46 | while (1); 47 | } 48 | imu.enableDefault(); 49 | } 50 | 51 | void loop() 52 | { 53 | mag.read(); 54 | imu.read(); 55 | 56 | /* 57 | When given no arguments, the heading() function returns the angular 58 | difference in the horizontal plane between a default vector (the 59 | +X axis) and north, in degrees. 60 | */ 61 | float heading = computeHeading(); 62 | 63 | /* 64 | To use a different vector as a reference, use the version of 65 | computeHeading() that takes a vector argument; for example, call it like this 66 | to use the -Z axis as a reference: 67 | */ 68 | //float heading = computeHeading((LIS3MDL::vector){0, 0, -1}); 69 | 70 | Serial.println(heading); 71 | delay(100); 72 | } 73 | 74 | /* 75 | Returns the angular difference in the horizontal plane between the 76 | "from" vector and north, in degrees. 77 | 78 | Description of heading algorithm: 79 | Shift and scale the magnetic reading based on calibration data to find 80 | the North vector. Use the acceleration readings to determine the Up 81 | vector (gravity is measured as an upward acceleration). The cross 82 | product of North and Up vectors is East. The vectors East and North 83 | form a basis for the horizontal plane. The From vector is projected 84 | into the horizontal plane and the angle between the projected vector 85 | and horizontal north is returned. 86 | */ 87 | template float computeHeading(LIS3MDL::vector from) 88 | { 89 | LIS3MDL::vector temp_m = {mag.m.x, mag.m.y, mag.m.z}; 90 | 91 | // copy acceleration readings from LSM6::vector into an LIS3MDL::vector 92 | LIS3MDL::vector a = {imu.a.x, imu.a.y, imu.a.z}; 93 | 94 | // subtract offset (average of min and max) from magnetometer readings 95 | temp_m.x -= ((int32_t)m_min.x + m_max.x) / 2; 96 | temp_m.y -= ((int32_t)m_min.y + m_max.y) / 2; 97 | temp_m.z -= ((int32_t)m_min.z + m_max.z) / 2; 98 | 99 | // compute E and N 100 | LIS3MDL::vector E; 101 | LIS3MDL::vector N; 102 | LIS3MDL::vector_cross(&temp_m, &a, &E); 103 | LIS3MDL::vector_normalize(&E); 104 | LIS3MDL::vector_cross(&a, &E, &N); 105 | LIS3MDL::vector_normalize(&N); 106 | 107 | // compute heading 108 | float heading = atan2(LIS3MDL::vector_dot(&E, &from), LIS3MDL::vector_dot(&N, &from)) * 180 / PI; 109 | if (heading < 0) heading += 360; 110 | return heading; 111 | } 112 | 113 | /* 114 | Returns the angular difference in the horizontal plane between a 115 | default vector (the +X axis) and north, in degrees. 116 | */ 117 | float computeHeading() 118 | { 119 | return computeHeading((LIS3MDL::vector){1, 0, 0}); 120 | } 121 | -------------------------------------------------------------------------------- /examples/Serial/Serial.ino: -------------------------------------------------------------------------------- 1 | /* 2 | The sensor outputs provided by the library are the raw 16-bit values 3 | obtained by concatenating the 8-bit high and low magnetometer data registers. 4 | They can be converted to units of gauss using the 5 | conversion factors specified in the datasheet for your particular 6 | device and full scale setting (gain). 7 | 8 | Example: An LIS3MDL gives a magnetometer X axis reading of 1292 with its 9 | default full scale setting of +/- 4 gauss. The GN specification 10 | in the LIS3MDL datasheet (page 8) states a conversion factor of 6842 11 | LSB/gauss (where LSB means least significant bit) at this FS setting, so the raw 12 | reading of 1292 corresponds to 1292 / 6842 = 0.1888 gauss. 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | LIS3MDL mag; 19 | 20 | char report[80]; 21 | 22 | void setup() 23 | { 24 | Serial.begin(9600); 25 | Wire.begin(); 26 | 27 | if (!mag.init()) 28 | { 29 | Serial.println("Failed to detect and initialize magnetometer!"); 30 | while (1); 31 | } 32 | 33 | mag.enableDefault(); 34 | } 35 | 36 | void loop() 37 | { 38 | mag.read(); 39 | 40 | snprintf(report, sizeof(report), "M: %6d %6d %6d", 41 | mag.m.x, mag.m.y, mag.m.z); 42 | Serial.println(report); 43 | 44 | delay(100); 45 | } 46 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | LIS3MDL KEYWORD1 2 | 3 | init KEYWORD2 4 | getDeviceType KEYWORD2 5 | enableDefault KEYWORD2 6 | writeReg KEYWORD2 7 | readReg KEYWORD2 8 | read KEYWORD2 9 | vector_cross KEYWORD2 10 | vector_dot KEYWORD2 11 | vector_normalize KEYWORD2 12 | 13 | device_LIS3MDL LITERAL1 14 | device_auto LITERAL1 15 | sa1_low LITERAL1 16 | sa1_high LITERAL1 17 | sa1_auto LITERAL1 18 | 19 | CTRL_REG1 LITERAL1 20 | CTRL_REG2 LITERAL1 21 | CTRL_REG3 LITERAL1 22 | CTRL_REG4 LITERAL1 23 | CTRL_REG5 LITERAL1 24 | STATUS_REG LITERAL1 25 | OUT_X_L LITERAL1 26 | OUT_X_H LITERAL1 27 | OUT_Y_L LITERAL1 28 | OUT_Y_H LITERAL1 29 | OUT_Z_L LITERAL1 30 | OUT_Z_H LITERAL1 31 | TEMP_OUT_L LITERAL1 32 | TEMP_OUT_H LITERAL1 33 | INT_CFG LITERAL1 34 | INT_SRC LITERAL1 35 | INT_THS_L LITERAL1 36 | INT_THS_H LITERAL1 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=LIS3MDL 2 | version=2.0.0 3 | author=Pololu 4 | maintainer=Pololu 5 | sentence=LIS3MDL magnetometer library 6 | paragraph=This is a library for the Arduino IDE that helps interface with ST's LIS3MDL magnetometer. 7 | category=Sensors 8 | url=https://github.com/pololu/lis3mdl-arduino 9 | architectures=* --------------------------------------------------------------------------------