Architecture overview — 9DoF motion sensor → OSC over Ethernet
A self-contained motion sensor "antenna": plug the MPU9150 module directly into the ESP32-PoE's UEXT connector, power the board over a single Ethernet cable (PoE — no separate power supply), and it streams accelerometer, gyroscope, magnetometer, and fused orientation data as OSC to any receiving software (Max/MSP, Pure Data, TouchDesigner, etc.) at 50 Hz. Onboard gyroscope calibration runs automatically at boot, and a one-time (or as-needed) magnetometer calibration — triggered by a button hold or a remote OSC message — is persisted to flash so it survives reboots.
| Part | Reference |
|---|---|
| MCU board | Olimex ESP32-PoE (16 MB flash) |
| Motion sensor | Olimex MOD-MPU9150 (MPU-6050 accel/gyro + AK8975 magnetometer, in one package) |
| Connection | UEXT connector — plugs directly, no wiring |
| Network | RJ45 Ethernet, powered over PoE |
| I2C pins (UEXT) | SDA = GPIO13, SCL = GPIO16 |
| Onboard LED | GPIO33, active-high |
| BTN1 | GPIO34, active-low, external pull-up |
Incoming OSC on port 8888 (/mpu/calibrate) can trigger a magnetometer calibration session on
the board at any time, independent of the outgoing data stream.
The board runs a fixed 50 Hz loop (LOOP_MS = 20):
puara-gestures) that fuses accelerometer, gyroscope, and
magnetometer into roll, pitch, and tilt-compensated yaw.Hard-iron interference (nearby metal, speakers, power supplies) shifts the magnetometer's zero point, which throws off yaw. Calibration removes that constant offset — needed once at first setup, and again whenever the instrument's physical configuration changes.
Calibration can be started two ways — holding the physical button, or sending an OSC message from the same network the board streams to — so it can be triggered either at the instrument itself or remotely from whatever software is already receiving its data. During the 30-second window, rotating the sensor through every orientation (a figure-8 motion covering all axes works well) lets the firmware record the true minimum and maximum reading on each magnetometer axis; the midpoint of each axis's min/max becomes that axis's bias. The result is written to non-volatile storage and survives power cuts and firmware re-flashes.
| LED behaviour | Meaning |
|---|---|
| Off | No Ethernet link |
| Slow blink (1 Hz) | Connected and streaming |
| Solid ON | BTN1 is being held — keep holding to start calibration |
| Fast blink (4 Hz) | Magnetometer calibration in progress |
| Solid for 1 second | Calibration just completed successfully |
| Address | Type | Unit | Description |
|---|---|---|---|
/mpu/accel/x /mpu/accel/y /mpu/accel/z | float | g | Raw accelerometer |
/mpu/gyro/x /mpu/gyro/y /mpu/gyro/z | float | °/s | Gyroscope, bias-corrected |
/mpu/mag/x /mpu/mag/y /mpu/mag/z | float | unit sphere | Magnetometer, normalized; offset before calibration, centered on origin after |
/mpu/euler/roll | float | ° (0–360) | Rotation around X |
/mpu/euler/pitch | float | ° (−90–+90) | Rotation around Y |
/mpu/euler/yaw | float | ° (0–360) | Heading, tilt-compensated |
/mpu/tilt | float | ° (0–180) | Combined tilt magnitude, sqrt(roll² + pitch²), clamped |
/mpu/btn1 | int | — | 1 while BTN1 is held, else 0 |
/mpu/cal/status | int | — | 0 = uncalibrated, 1 = calibrating, 2 = calibrated |
| Address | Arguments | Description |
|---|---|---|
/mpu/calibrate | (none) | Starts a 30-second magnetometer calibration session |
| Parameter | Location | Default | Effect |
|---|---|---|---|
| Send rate | LOOP_MS | 20 (50 Hz) | Lower = faster updates; don't go below 10 (100 Hz) |
| Kalman process noise (Q) | kalman(Q, R) | 0.001 | How much the filter trusts gyroscope integration; lower = smoother but slower to respond |
| Kalman measurement noise (R) | kalman(Q, R) | 0.01 | How much the filter trusts accel/mag corrections; higher = smoother but drifts more under disturbance |
| Gyroscope DLPF bandwidth | R_CONFIG register write | 0x04 (~21 Hz) | 0x02=98 Hz (fast movement) … 0x05=10 Hz (near-static); current setting suits slow, deliberate movement |
├── boards/
│ └── esp32-poe-16mb.json custom PlatformIO board definition
├── examples/
│ ├── ESP32_PoE_Ethernet_SD_Card_Arduino.ino
│ ├── ESP32_PoE_WebServer_Demo.ino
│ └── MagnetometreCalibrationDemo.cpp
├── mpu9150_demo/ original bare sensor demo
├── src/
│ └── main.cpp firmware (edit this)
└── platformio.ini
The examples/ and mpu9150_demo/ folders are bundled reference material, not part
of the firmware that actually runs — each comes from a different origin and is kept for reference rather
than executed:
mpu9150_demo.ino — the original bare-metal MPU-9150 I2C register demo
(public domain, by Arduino user "frtrobotik" / Tobias Hübner, Olimexino edition by Chris B.) that the
sensor-register logic in this project traces back to.MagnetometreCalibrationDemo.cpp — a magnetometer-calibration reference
example from a different IDMIL project (GuitarAMI, Edu Meneses, built on the Puara templates), kept here
as a comparison point for calibration UX on a fuller IMU stack (LSM9DS1/BNO080).ESP32_PoE_WebServer_Demo.ino — a general-purpose webserver demo for the
Olimex ESP32-PoE board (controls various Olimex UEXT modules), originally by David Bird, adapted for this
board — unrelated to the MPU9150 but useful as an ESP32-PoE peripheral reference.ESP32_PoE_Ethernet_SD_Card_Arduino.ino — a combined SD-card + Ethernet
bring-up demo for the same board.
boards/esp32-poe-16mb.json defines the Olimex ESP32-PoE's 16 MB-flash variant for PlatformIO,
since it isn't one of the built-ins: correct partition table and linker script for 16 MB flash, the Ethernet
PHY wiring specific to this board (LAN8720 over RMII, MDC/MDIO pins, power pin, clock mode), and upload
parameters (921,600 baud, esptool/espota).
Managed automatically by PlatformIO: