> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e-spin.fr/llms.txt
> Use this file to discover all available pages before exploring further.

# Read IMU Raw Data

> Pull live accelerometer, gyroscope, and temperature data from the onboard QMI8658.

<Note>
  This tutorial assumes you have already installed the QMI8658 library and know how to flash the board. See the [library installation guide](#) and [getting started guide](#) if needed.
</Note>

## What is an IMU?

An **Inertial Measurement Unit** (IMU) is a sensor that measures motion. The QMI8658 on E-Spin is a 6-axis IMU, meaning it provides two types of measurements:

* **Accelerometer (3 axes)** — measures proper acceleration, i.e. the force applied to the sensor. At rest flat on a table, it reads \~1g on the Z axis (Earth's gravity). In free fall, it reads 0g. This lets you determine tilt and detect sudden impacts.

* **Gyroscope (3 axes)** — measures angular velocity in degrees per second (°/s). It tells you *how fast* the sensor is rotating around each axis, not the absolute angle.

Together, they're the foundation for any motion-aware application: gesture detection, orientation tracking, spin speed measurement, step counting, vibration analysis.

The QMI8658 also includes a **temperature sensor** — useful for compensating sensor drift, since gyroscope bias shifts with temperature.

**On E-Spin specifically**, the IMU is what makes the board aware that it's spinning. The gyroscope Z-axis is your RPM source. The accelerometer detects taps, drops, and orientation.

***

## Library

This tutorial uses the **QMI8658 Arduino Library** by [Lahav Gahali](https://github.com/lahavg/QMI8658-Arduino-Library), MIT licensed.

```
lahavg/QMI8658 @ ^1.0.1
```

Add to your `platformio.ini`:

```ini theme={null}
lib_deps =
    lahavg/QMI8658@^1.0.1
```

***

## Wiring

No external wiring needed. The QMI8658 is connected directly to the ESP32-C3 on the PCB.

Check your board's silkscreen or schematic for the exact pin assignment. On current E-Spin hardware:

| Signal | GPIO |
| ------ | ---- |
| SDA    | 5    |
| SCL    | 4    |

The I2C address is `0x6A` (SA0 low) or `0x6B` (SA0 high) — the library auto-detects it.

***

## Code

```cpp theme={null}
#include <Wire.h>
#include <QMI8658.h>

#define SDA_PIN 5
#define SCL_PIN 4

QMI8658 imu;

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  if (!imu.begin(SDA_PIN, SCL_PIN)) {
    Serial.println("QMI8658 not found. Check wiring and I2C address.");
    while (1) delay(1000);
  }

  Serial.print("QMI8658 detected. Device ID: 0x");
  Serial.println(imu.getWhoAmI(), HEX);  // Should print 0x05

  // Configure ranges
  imu.setAccelRange(QMI8658_ACCEL_RANGE_8G);      // ±8g — enough headroom for taps
  imu.setGyroRange(QMI8658_GYRO_RANGE_2048DPS);   // ±2048 dps — spinner needs this

  // Output data rate: 1000Hz internally, we'll read slower
  imu.setAccelODR(QMI8658_ACCEL_ODR_1000HZ);
  imu.setGyroODR(QMI8658_GYRO_ODR_1000HZ);

  // Set units
  imu.setAccelUnit_mps2(true);   // m/s²
  imu.setGyroUnit_dps(true);     // degrees per second

  imu.enableSensors(QMI8658_ENABLE_ACCEL | QMI8658_ENABLE_GYRO);

  Serial.println("Accel (m/s²) | Gyro (dps) | Temp (°C)");
  Serial.println("-------------------------------------------");
}

void loop() {
  QMI8658_Data data;

  if (imu.readSensorData(data)) {
    Serial.printf("A: %6.2f %6.2f %6.2f | G: %8.2f %8.2f %8.2f | T: %.1f\n",
      data.accelX, data.accelY, data.accelZ,
      data.gyroX,  data.gyroY,  data.gyroZ,
      data.temperature
    );
  }

  delay(50); // ~20Hz print rate
}
```

***

## Walkthrough

### What the ranges mean

**Accelerometer range** controls how large an acceleration the sensor can measure before clipping. `±8g` covers normal use — a hard tap registers around 3–5g. If you were mounting this on a drone or rocket, you'd go higher.

**Gyroscope range** is critical for E-Spin. A spinner at 300 RPM rotates at 1800°/s. At 500 RPM that's 3000°/s, which exceeds `±2048 dps`. For very fast spins, this channel will saturate — the fusion filter tutorial addresses this.

### Reading the output

Open the Serial Monitor at 115200 baud. You should see a table like:

```
A:  -0.12   0.08   9.82 | G:    0.43   -1.62    0.28 | T: 28.7
A:  -0.11   0.09   9.81 | G:    0.51   -1.55    0.31 | T: 28.7
```

* **A (accel):** at rest flat, Z ≈ 9.81 m/s² (gravity). Tilt the board and the gravity vector redistributes across X/Y/Z.
* **G (gyro):** at rest, values should be near zero with a small constant offset called **bias** — that's normal and expected. Rotate the board and the relevant axis spikes.
* **T:** die temperature in °C. Gyro bias drifts with temperature; the fusion filter tutorial will handle compensation.

### Checking the device ID

`imu.getWhoAmI()` should return `0x05`. If it returns `0x00` or `0xFF`, the sensor isn't responding — run the I2C scanner first to confirm the address and connection.

### Gyro range for spinning

The E-Spin board is designed to spin. Use `QMI8658_GYRO_RANGE_2048DPS` as your default — lower ranges will saturate and give you flat-topped garbage data the moment you flick it. If you're only reading at rest (tap detection, orientation), `512DPS` gives better resolution.

***

## What the raw data cannot tell you

Raw gyroscope values give you rotation *rate*, not angle. Raw accelerometer values give you force, which blends gravity and motion together. Neither alone gives you a clean orientation.

To get tilt angles, heading, or quaternions — that's the next tutorial.

<Card title="IMU Fusion Filter" icon="axis-3d" href="/projects/imu-fusion">
  Combine accel and gyro with a complementary or Madgwick filter to get stable orientation angles.
</Card>
