Skip to content

Instantly share code, notes, and snippets.

@somebox
Last active June 19, 2025 14:17
Show Gist options
  • Save somebox/a230246b91b266928b07392cbc469374 to your computer and use it in GitHub Desktop.
Save somebox/a230246b91b266928b07392cbc469374 to your computer and use it in GitHub Desktop.
LSM9DS1 IMU sensor I2C reference guide

This guide to the LSM9DS1 was written with help of the datasheet, offical STMicro driver source, and other libraries. It is for low level troubleshooting and use of this sensor. Gemini 2.5 pro was used to help summarize and cross-check.


The Complete Arduino I2C Guide for the LSM9DS1

(A Merged Technical Reference Based on the Official ST Driver and Community Examples)

This guide provides a robust, end-to-end technical reference for Arduino developers who wish to perform a direct I2C implementation of the LSM9DS1. It combines the safety patterns from the official STMicroelectronics C driver with practical code examples and register maps, creating a single, authoritative resource for your project.

1. Hardware Connection & Troubleshooting

Getting the hardware right is the first and most critical step. Incorrect wiring is the most common source of initialization failure.

  • I2C Addresses: The LSM9DS1 acts as two independent I2C devices. The addresses are set by the SDO_A/G and SDO_M pins.

    • Accelerometer/Gyroscope (AG):
      • 0x6A if SDO_A/G is connected to GND (default on many boards).
      • 0x6B if SDO_A/G is connected to VDD.
    • Magnetometer (M):
      • 0x1C if SDO_M is connected to GND (default on many boards).
      • 0x1E if SDO_M is connected to VDD.
  • Essential I2C Enable Pins: To use the I2C interface, the SPI Chip Select (CS) pins must be pulled HIGH to disable SPI mode.

    • CS_A/G -> connect to VDD (3.3V).
    • CS_M -> connect to VDD (3.3V).
    • Troubleshooting: If you get no response on the I2C bus, this is the most likely cause. Most breakout boards handle this for you, but it is mandatory if you are using the bare chip.
  • Voltage: The LSM9DS1 is a 3.3V device. Do not connect its VDD, VDDIO, SCL, or SDA pins to 5V. This can permanently damage the sensor. Use a logic level shifter if you are using a 5V microcontroller like an Arduino Uno.

  • I2C Pull-up Resistors: While the Arduino Wire library has internal pull-ups, they are often too weak for reliable communication. For stable operation, it is highly recommended to use external 4.7kΩ pull-up resistors on the SCL and SDA lines, connected to your 3.3V supply.

2. Key Register Map

Here are the essential registers you will interact with.

Accelerometer and Gyroscope (AG) - I2C Addr: 0x6A or 0x6B

Name Address Description
WHO_AM_I 0x0F Device ID. Reads 0x68.
CTRL_REG1_G 0x10 Gyroscope control (ODR, Full-Scale).
STATUS_REG 0x17 Data-ready flags for AG.
OUT_X_L_G 0x18 Gyroscope X-axis low byte.
CTRL_REG6_XL 0x20 Accelerometer control (ODR, Full-Scale).
CTRL_REG8 0x22 BDU, Reset, and other controls.
OUT_X_L_XL 0x28 Accelerometer X-axis low byte.
FIFO_CTRL 0x2E FIFO control and mode settings.
FIFO_SRC 0x2F FIFO status (unread samples, full, empty).

Magnetometer (M) - I2C Addr: 0x1C or 0x1E

Name Address Description
WHO_AM_I_M 0x0F Device ID. Reads 0x3D.
CTRL_REG1_M 0x20 Magnetometer control (ODR, performance).
CTRL_REG2_M 0x21 Full-Scale, Reset.
CTRL_REG3_M 0x22 Operating mode (Continuous, Power-down).
CTRL_REG5_M 0x24 Block Data Update (BDU).
STATUS_REG_M 0x27 Data-ready flags for M.
OUT_X_L_M 0x28 Magnetometer X-axis low byte.

3. The Robust Initialization Sequence

Follow this official driver-based sequence in your setup() function to guarantee the sensor starts in a known, stable state.

Step 1: Verify Communication with WHO_AM_I This is your first-pass diagnostic. Before any configuration, confirm you can talk to both devices.

// In your setup()
Wire.begin();

// Check Accel/Gyro WHO_AM_I
uint8_t who_am_i_ag;
readRegister(LSM9DS1_AG_ADDRESS, LSM9DS1_WHO_AM_I, &who_am_i_ag);
if (who_am_i_ag != 0x68) {
  Serial.println("AG WHO_AM_I check failed. Halting.");
  while(1);
}

// Check Mag WHO_AM_I
uint8_t who_am_i_m;
readRegister(LSM9DS1_M_ADDRESS, LSM9DS1_WHO_AM_I_M, &who_am_i_m);
if (who_am_i_m != 0x3D) {
  Serial.println("Mag WHO_AM_I check failed. Halting.");
  while(1);
}
  • Troubleshooting: If this fails, your circuit is wrong (check I2C addresses, CS pins, wiring) or the I2C bus is non-functional.

Step 2: Reset The Device (Driver Best Practice) The ST driver resets the device to ensure a clean state. You must wait for the reset bit to clear itself to confirm completion.

  • Accel/Gyro Reset: Set SW_RESET bit in CTRL_REG8 (0x22), then poll until it reads 0.
  • Magnetometer Reset: Set REBOOT bit in CTRL_REG2_M (0x21), then poll until it reads 0.

Step 3: Enable Block Data Update (BDU) This is critical for data integrity. BDU prevents the sensor's high and low output bytes from being updated mid-read, which would corrupt your data. Always enable this.

// Enable BDU for Accel/Gyro
writeRegister(LSM9DS1_AG_ADDRESS, LSM9DS1_CTRL_REG8, 0x44); // BDU enabled, register auto-increment enabled

// Enable BDU for Mag
writeRegister(LSM9DS1_M_ADDRESS, LSM9DS1_CTRL_REG5_M, 0x40); // BDU enabled

Step 4: Configure and Enable Sensors The sensors are off by default. To enable them, you must configure their Output Data Rate (ODR) and operating mode.

// Example: Enable Gyro at 119 Hz, 245 dps
writeRegister(LSM9DS1_AG_ADDRESS, LSM9DS1_CTRL_REG1_G, 0x60); // 119 Hz ODR, 245 dps

// Example: Enable Accel at 119 Hz, 2g
writeRegister(LSM9DS1_AG_ADDRESS, LSM9DS1_CTRL_REG6_XL, 0x60); // 119 Hz ODR, 2g

// Example: Enable Mag at 80 Hz, continuous mode
writeRegister(LSM9DS1_M_ADDRESS, LSM9DS1_CTRL_REG1_M, 0x1C); // Temp comp disabled, high-perf mode, 80 Hz ODR
writeRegister(LSM9DS1_M_ADDRESS, LSM9DS1_CTRL_REG3_M, 0x00); // Continuous-conversion mode

4. Data Update and Reading Pattern

The most robust way to get live data is to separate checking for new data from reading the data.

Part A: Poll the Status Register for Data-Ready Flags Do not read the output registers blindly. First, check the status register to see if a fresh sample is available.

  • XLDA bit (bit 0) in STATUS_REG (0x17) -> New Accelerometer data
  • GDA bit (bit 1) in STATUS_REG (0x17) -> New Gyroscope data
  • ZYXDA bit (bit 3) in STATUS_REG_M (0x27) -> New Magnetometer data

Part B: Read the Multi-Byte Sensor Data Once a data-ready flag is confirmed, read all 6 bytes for that sensor in a single I2C transaction.

// In your loop()
uint8_t status_ag;
readRegister(LSM9DS1_AG_ADDRESS, LSM9DS1_STATUS_REG, &status_ag);

if (status_ag & 0x01) { // Check XLDA bit
  uint8_t data_buf[6];
  readRegisters(LSM9DS1_AG_ADDRESS, LSM9DS1_OUT_X_L_XL, 6, data_buf);
  int16_t ax = (int16_t)(data_buf[1] << 8 | data_buf[0]);
  int16_t ay = (int16_t)(data_buf[3] << 8 | data_buf[2]);
  int16_t az = (int16_t)(data_buf[5] << 8 | data_buf[4]);
  // Now convert raw data to g's
}

if (status_ag & 0x02) { // Check GDA bit
  // ... similar read from OUT_X_L_G (0x18) ...
}

uint8_t status_m;
readRegister(LSM9DS1_M_ADDRESS, LSM9DS1_STATUS_REG_M, &status_m);

if (status_m & 0x08) { // Check ZYXDA bit
  // ... similar read from OUT_X_L_M (0x28) ...
}
  • Troubleshooting:
    • Data never updates: You are not polling the status registers.
    • Data is 0 or -1: This signals a failed I2C read. Check wiring and addresses.
    • Data is nonsensical: You forgot to enable BDU, or you are misinterpreting the two's complement data.

5. Converting Raw Data to Physical Units

The raw int16_t values must be scaled by their sensitivity to get meaningful units like g, dps, and gauss. The sensitivity depends on the full-scale range you configured.

Accelerometer Sensitivity (mg/LSB)

Full-Scale Sensitivity
±2 g 0.061
±4 g 0.122
±8 g 0.244
±16 g 0.732
float acceleration_g = raw_value * sensitivity / 1000.0;

Gyroscope Sensitivity (mdps/LSB)

Full-Scale Sensitivity
245 dps 8.75
500 dps 17.50
2000 dps 70.00
float angular_rate_dps = raw_value * sensitivity / 1000.0;

Magnetometer Sensitivity (mgauss/LSB)

Full-Scale Sensitivity
±4 gauss 0.14
±8 gauss 0.29
±12 gauss 0.43
±16 gauss 0.58
float magnetic_field_gauss = raw_value * sensitivity / 1000.0;

6. Advanced Topic: Using the FIFO

The LSM9DS1 includes a 32-slot FIFO buffer that can store accelerometer and gyroscope readings. This is extremely useful for power saving, as the microcontroller can sleep and wake up only when the buffer is full or reaches a certain threshold, then read all the samples in a burst.

  • To Enable: Set the FIFO_EN bit in CTRL_REG9 (0x23).
  • To Configure: Use the FIFO_CTRL register (0x2E) to set the mode (e.g., Bypass, FIFO mode, Continuous mode) and the watermark threshold.
  • To Check Status: Read the FIFO_SRC register (0x2F) to see how many unread samples (FSS bits) are in the buffer. The FTH bit will be set when the number of samples meets your watermark threshold.
  • To Read: Read from the same output registers (e.g., OUT_X_L_G). Each read from the output registers will pull the next value from the FIFO instead of the live sensor value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment