The MPU6000 and MPU6050 are compact 6-axis motion tracking sensors that combine a 3-axis accelerometer and a 3-axis gyroscope on a single chip. They measures linear acceleration and rotational velocity. They communicate via I²C (or SPI depending on model). It's a cheap and popular choice for projects that need motion detection, orientation sensing, or gesture control.
Warning
This device is allegedly obsolete. It is quoted as being noisy and outdated. However they are still cheap, widely available, and good enough for many projects such as mine, making a driver worth the time to write.
A practical implementation of this device is shown in this 'Digital Dice' project.
This has gone a bit further than originally intended. The tech in this chip is genuinely impressive, and going down this rabbit hole has been educational. Much of the coolest stuff is undocumented, and/or, like MotionApps, behind an agreement/wall with TDK that no longer exists.
As a baseline, the base class Mpu6050 (in
/src/mpu6050.toit) contains any/all features without
resorting to DMP/MotionApps. Non-DMP (eg, in toit code in the driver/class)
implementations of features found in DMP funtions are also here. It functions
well so far for individual reads, interrupts, motion events, fifo, etc, even if
it might be improved over time.
In the development branch) the following are being worked on:
- Base
mpu6050class:- Giving MPU6050 3 more axes: The device has an auxiliary independent I2C bus, hosted on pins XDA/XCL. They're designed to put an I2C magnetometer behind them to get full AHRS etc. Whilst devices with magnetometers built-in already exist, this is still a cheap and functional method.
- Self-Test/Self-Calibration features
- Free Fall - exists but not well tested yet. (Needs a more robust build than a solderless breadboard.)
- X/Y/Z Fine Gain Control.
- DMP version
mpu6050-dmp-ma612of the class:- DMP: 'Digital Motion Processor' and 'Motion Fusion' features. As the name
implies, its being based on the MotionApps 6.12 sourced from
i2cdevlib and the work put in
at Jeff Rowberg's
repo. Status: in progress.
- 'Tap' Gesture is underway, 'Step Counter' is next.
- FIFO buffer: This currently works, although consuming the information from it, and controlling the rates and sources of inforation appearing in it is still not quite there. It features strongly in how DMP features are consumed, so is actively being worked on.
- Full AHRS implementation: This was interesting and expectation is that this will be completely possible. Status: Some time away for now. See: Open Source IMU and AHRS algorithms and Fusion Library
- Calibration: Much better features and capabilities exist when using DMP. This is also being surfaced.
- DMP: 'Digital Motion Processor' and 'Motion Fusion' features. As the name
implies, its being based on the MotionApps 6.12 sourced from
i2cdevlib and the work put in
at Jeff Rowberg's
repo. Status: in progress.
Each gyroscope sample from the MPU6050 gives angular velocity, i.e., how fast
the device is rotating around each axis at that moment. its usually represented
in degrees/second for each of the three axes. Note that this is not a universal
orientation, but a measure of change of orientation.
This 3-part measurement is obtained using Toit
Math library's
Point3f object. Gyroscope
measurement can be obtained by:
// Required for math objects
import math
// I2C setup omitted
new-gyro := ?
new-gyro = mpu6050-driver.read-gyroscope // returns math.Point3f object
// Print single measurement
print "$(%0.2f new-gyro.x)deg/sec x"
print "$(%0.2f new-gyro.y)deg/sec y"
print "$(%0.2f new-gyro.z)deg/sec z"One measurement from the accelerometer on the MPU6050 represents the
instantaneous acceleration being experienced by the sensor, split along its
three orthogonal sensing axes (X, Y, Z). Similar to the gyroscope, the xyz data
is provided in a Toit Point3f
object, as per this example:
// Required for math objects
import math
// I2C setup omitted
new-accel := ?
new-accel = mpu6050-driver.read-acceleration // returns math.Point3f object
// Print single measurement
print "$(%0.2f new-accel.x)g x"
print "$(%0.2f new-accel.y)g y"
print "$(%0.2f new-accel.z)g z"See the 'continuous-accel-read' example. Note that when the device is sitting there doing nothing physical, the axis representing gravity will remain at the value of g (gravitational acceleration - 9.80665 m/s²), for that axis:
[jaguar] INFO: program ea3248cd-18e0-7373-4ee4-0cff10ff4ebe started
Found Mpu60x0 on 0x68
get-whoami returned: 0x34
get-temperature returned: 20.483c
execute-gyro-self-test now:
read-accel returned: 1.0459x.g 0.0337y.g 0.0269z.g
read-accel returned: 1.0437x.g 0.0312y.g 0.0295z.g
read-accel returned: 1.0488x.g 0.0312y.g 0.0288z.g
read-accel returned: 1.0447x.g 0.0234y.g 0.0276z.g
read-accel returned: 1.0388x.g 0.0386y.g 0.0247z.g
read-accel returned: 1.0457x.g 0.0234y.g 0.0156z.g
read-accel returned: 1.0457x.g 0.0356y.g 0.0273z.g
...
This device can provide alerts when going from zero motion into motion, and vice versa. There are thresholds determining how much motion is necessary to trigger each of these. Its a somewhat undocumented feature however it functions very well using information from the community. A simple example:
// I2C setup omitted
// MOVEMENT DETECTION: Suggested starting values 20–40mg, for 20–50ms.
STILL-TO-MOTION-MG := 40 // force required to register motion. Bigger value = more movement required.
STILL-TO-MOTION-MS := 5 // duration of that force required to register motion. = Bigger value = movement required for longer.
// STILL DETECTION: Suggested starting values 5–10mg, for 600ms.
MOTION-TO-STILL-MG := 10 // forces on the device need to be less than this many milli-g's.
MOTION-TO-STILL-MS := 576 // ... for at least this duration of milliseconds.
// Configure Interrupt Pin, Defaults, and wake MPU6050
interrupt-pin = gpio.Pin interrupt-pin-number --input --pull-down
mpu6050-driver.set-clock-source Mpu6050-dmp-ma612.CLOCK-SRC-INTERNAL-8MHZ
mpu6050-driver.wakeup-now
// Reset all internal signal paths
mpu6050-driver.reset-gyroscope
mpu6050-driver.reset-accelerometer
mpu6050-driver.reset-temperature
// Disable Unused Bits
mpu6050-driver.disable-temperature
mpu6050-driver.disable-gyroscope
// Configure Digital High Pass Filter - so slow tilt doesn’t look like motion.
mpu6050-driver.set-accelerometer-high-pass-filter Mpu6050-dmp-ma612.ACCEL-HPF-0-63HZ
// Set Zero-Motion to Motion Detection (ms = milliseconds, mg=milli G's)
mpu6050-driver.set-motion-detection-duration-ms STILL-TO-MOTION-MS
mpu6050-driver.set-motion-detection-threshold-mg STILL-TO-MOTION-MG
mpu6050-driver.enable-interrupt-motion-detection
// Set Motion to Zero Motion Detection (ms = milliseconds, mg=milli G's)
mpu6050-driver.set-zero-motion-detection-duration-ms MOTION-TO-STILL-MS
mpu6050-driver.set-zero-motion-detection-threshold-mg MOTION-TO-STILL-MG
mpu6050-driver.enable-interrupt-zero-motion-detection
// Set interrupt pin to go high when activated
mpu6050-driver.set-interrupt-pin-active-high
mpu6050-driver.disable-fsync-pin
// Set up interaction - keep pin active until values read.
mpu6050-driver.enable-interrupt-pin-latching
mpu6050-driver.set-interrupt-pin-read-clears
mpu6050-driver.set-dlpf-config Mpu6050-dmp-ma612.CONFIG-DLPF-3
// Set decrement rates and delay for freefall and motion detection
mpu6050-driver.set-motion-detection-count-decrement-rate 1
mpu6050-driver.set-free-fall-count-decrement-rate 1
mpu6050-driver.set-acceleration-wake-delay-ms 5At this point, the interrupt pin will fire once at the start of motion, and fire
again when motion has stopped. The following can be used to determine if either
stopping or starting movement, using the statics MOT-DETECT-MOT-TO-ZMOT and MOT-DETECT-ZMOT-TO-MOT if required:
motdt-status = mpu6050-driver.get-motion-detect-status
// Motion to Zero Motion (Stopping):
if (motdt-status & Mpu6050-dmp-ma612.MOT-DETECT-MOT-TO-ZMOT) != 0:
print " Motion stopping..."The device has its own thermal sensor. Its range is good for –40C to +85C with increments of 0.00294C, however the overall accuracy is specified at +/- 1C. Temperature can be obtained with:
// I2C setup omitted
print " get-temperature: $(%0.3f driver.read-temperature)c" // e.g. 24.213cMPU6050 has a 1024 byte FIFO buffer which allows reads to occur in bursts. This
can be enabled using enable-fifo-buffer and disable-fifo-buffer. This functions well, and is used for DMP, but more on this soon.
Links to sources of information about its undocumented features:
- Arduino Forums Post 'Reverse Engineering Undocumented MPU6050 Registers'
- MPU6050 register list on I2CDevLib.com
- MPU6050 Explained an excellent article about the boards function and use.
- MPU6050 Driver for the ROS 'Robot Operating System'.
- jrowberg's Code I2C Device Library
- Datasheet
- Register Map
If there are any issues, changes, or any other kind of feedback, please raise an issue. Feedback is welcome and appreciated!
- This driver has been written and tested with an unbranded module as pictured.
- All trademarks belong to their respective owners.
- No warranties for this work, express or implied.
- Credit to imliubo for their Toit implementation for sibiling device MPU6886.
- AI has been used for reviews, analysing & compiling data/results, and assisting with ensuring accuracy.
- Florian for the tireless help and encouragement
- The wider Toit developer team (past and present) for a truly excellent product
One would assume you are here because you know what Toit is. If you dont:
Toit is a high-level, memory-safe language, with container/VM technology built specifically for microcontrollers (not a desktop language port). It gives fast iteration (live reloads over Wi-Fi in seconds), robust serviceability, and performance that’s far closer to C than typical scripting options on the ESP32. [link]
