From 61254121dcf56fb41c84b61d6cb8097ae7ffca3e Mon Sep 17 00:00:00 2001 From: gogacoder Date: Wed, 3 Jan 2024 21:11:06 +0700 Subject: [PATCH] initial commit --- MPU6050.cpp | 3397 +++++++++++++++++ MPU6050.h | 852 +++++ MPU6050_6Axis_MotionApps20.cpp | 616 +++ MPU6050_6Axis_MotionApps20.h | 153 + MPU6050_6Axis_MotionApps612.cpp | 617 +++ MPU6050_6Axis_MotionApps612.h | 153 + MPU6050_9Axis_MotionApps41.cpp | 887 +++++ MPU6050_9Axis_MotionApps41.h | 153 + examples/IMU_Zero/IMU_Zero.ino | 358 ++ examples/MPU6050_DMP6/MPU6050_DMP6.ino | 345 ++ .../Processing/MPUTeapot/MPUTeapot.pde | 242 ++ .../MPU6050_DMP6_ESPWiFi.ino | 373 ++ .../Processing/MPUOSCTeapot/MPUOSCTeapot.pde | 188 + .../MPU6050_DMP6_Ethernet.ino | 545 +++ .../MPU6050_DMP6_using_DMP_V6.12.ino | 368 ++ .../MPUplane/MPUplane.pde | 189 + .../MPUplane/data/biplane.obj | 2239 +++++++++++ .../MPUplane/data/diffuse_512.png | Bin 0 -> 361095 bytes examples/MPU6050_raw/MPU6050_raw.ino | 153 + helper_3dmath.h | 216 ++ library.json | 18 + 21 files changed, 12062 insertions(+) create mode 100644 MPU6050.cpp create mode 100644 MPU6050.h create mode 100644 MPU6050_6Axis_MotionApps20.cpp create mode 100644 MPU6050_6Axis_MotionApps20.h create mode 100644 MPU6050_6Axis_MotionApps612.cpp create mode 100644 MPU6050_6Axis_MotionApps612.h create mode 100644 MPU6050_9Axis_MotionApps41.cpp create mode 100644 MPU6050_9Axis_MotionApps41.h create mode 100644 examples/IMU_Zero/IMU_Zero.ino create mode 100644 examples/MPU6050_DMP6/MPU6050_DMP6.ino create mode 100644 examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde create mode 100644 examples/MPU6050_DMP6_ESPWiFi/MPU6050_DMP6_ESPWiFi.ino create mode 100644 examples/MPU6050_DMP6_ESPWiFi/Processing/MPUOSCTeapot/MPUOSCTeapot.pde create mode 100644 examples/MPU6050_DMP6_Ethernet/MPU6050_DMP6_Ethernet.ino create mode 100644 examples/MPU6050_DMP6_using_DMP_V6.12/MPU6050_DMP6_using_DMP_V6.12.ino create mode 100644 examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/MPUplane.pde create mode 100644 examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/data/biplane.obj create mode 100644 examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/data/diffuse_512.png create mode 100644 examples/MPU6050_raw/MPU6050_raw.ino create mode 100644 helper_3dmath.h create mode 100644 library.json diff --git a/MPU6050.cpp b/MPU6050.cpp new file mode 100644 index 0000000..ae14500 --- /dev/null +++ b/MPU6050.cpp @@ -0,0 +1,3397 @@ +// I2Cdev library collection - MPU6050 I2C device class +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 8/24/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021-09-27 - split implementations out of header files, finally +// 2019-07-08 - Added Auto Calibration routine +// ... - ongoing debug release + +// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE +// DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF +// YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING. + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#include "MPU6050.h" + +/** Specific address constructor. + * @param address I2C address, uses default I2C address if none is specified + * @see MPU6050_DEFAULT_ADDRESS + * @see MPU6050_ADDRESS_AD0_LOW + * @see MPU6050_ADDRESS_AD0_HIGH + */ +MPU6050_Base::MPU6050_Base(uint8_t address, void *wireObj):devAddr(address), wireObj(wireObj) { +} + +/** Power on and prepare for general usage. + * This will activate the device and take it out of sleep mode (which must be done + * after start-up). This function also sets both the accelerometer and the gyroscope + * to their most sensitive settings, namely +/- 2g and +/- 250 degrees/sec, and sets + * the clock source to use the X Gyro for reference, which is slightly better than + * the default internal clock source. + */ +void MPU6050_Base::initialize() { + setClockSource(MPU6050_CLOCK_PLL_XGYRO); + setFullScaleGyroRange(MPU6050_GYRO_FS_250); + setFullScaleAccelRange(MPU6050_ACCEL_FS_2); + setSleepEnabled(false); // thanks to Jack Elston for pointing this one out! +} + +/** Verify the I2C connection. + * Make sure the device is connected and responds as expected. + * @return True if connection is valid, false otherwise + */ +bool MPU6050_Base::testConnection() { + return getDeviceID() == 0x34; +} + +// AUX_VDDIO register (InvenSense demo code calls this RA_*G_OFFS_TC) + +/** Get the auxiliary I2C supply voltage level. + * When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to + * 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to + * the MPU-6000, which does not have a VLOGIC pin. + * @return I2C supply voltage level (0=VLOGIC, 1=VDD) + */ +uint8_t MPU6050_Base::getAuxVDDIOLevel() { + I2Cdev::readBit(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_PWR_MODE_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the auxiliary I2C supply voltage level. + * When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to + * 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to + * the MPU-6000, which does not have a VLOGIC pin. + * @param level I2C supply voltage level (0=VLOGIC, 1=VDD) + */ +void MPU6050_Base::setAuxVDDIOLevel(uint8_t level) { + I2Cdev::writeBit(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_PWR_MODE_BIT, level, wireObj); +} + +// SMPLRT_DIV register + +/** Get gyroscope output rate divider. + * The sensor register output, FIFO output, DMP sampling, Motion detection, Zero + * Motion detection, and Free Fall detection are all based on the Sample Rate. + * The Sample Rate is generated by dividing the gyroscope output rate by + * SMPLRT_DIV: + * + * Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV) + * + * where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or + * 7), and 1kHz when the DLPF is enabled (see Register 26). + * + * Note: The accelerometer output rate is 1kHz. This means that for a Sample + * Rate greater than 1kHz, the same accelerometer sample may be output to the + * FIFO, DMP, and sensor registers more than once. + * + * For a diagram of the gyroscope and accelerometer signal paths, see Section 8 + * of the MPU-6000/MPU-6050 Product Specification document. + * + * @return Current sample rate + * @see MPU6050_RA_SMPLRT_DIV + */ +uint8_t MPU6050_Base::getRate() { + I2Cdev::readByte(devAddr, MPU6050_RA_SMPLRT_DIV, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set gyroscope sample rate divider. + * @param rate New sample rate divider + * @see getRate() + * @see MPU6050_RA_SMPLRT_DIV + */ +void MPU6050_Base::setRate(uint8_t rate) { + I2Cdev::writeByte(devAddr, MPU6050_RA_SMPLRT_DIV, rate, wireObj); +} + +// CONFIG register + +/** Get external FSYNC configuration. + * Configures the external Frame Synchronization (FSYNC) pin sampling. An + * external signal connected to the FSYNC pin can be sampled by configuring + * EXT_SYNC_SET. Signal changes to the FSYNC pin are latched so that short + * strobes may be captured. The latched FSYNC signal will be sampled at the + * Sampling Rate, as defined in register 25. After sampling, the latch will + * reset to the current FSYNC signal state. + * + * The sampled value will be reported in place of the least significant bit in + * a sensor data register determined by the value of EXT_SYNC_SET according to + * the following table. + * + *
+ * EXT_SYNC_SET | FSYNC Bit Location
+ * -------------+-------------------
+ * 0            | Input disabled
+ * 1            | TEMP_OUT_L[0]
+ * 2            | GYRO_XOUT_L[0]
+ * 3            | GYRO_YOUT_L[0]
+ * 4            | GYRO_ZOUT_L[0]
+ * 5            | ACCEL_XOUT_L[0]
+ * 6            | ACCEL_YOUT_L[0]
+ * 7            | ACCEL_ZOUT_L[0]
+ * 
+ * + * @return FSYNC configuration value + */ +uint8_t MPU6050_Base::getExternalFrameSync() { + I2Cdev::readBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_EXT_SYNC_SET_BIT, MPU6050_CFG_EXT_SYNC_SET_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set external FSYNC configuration. + * @see getExternalFrameSync() + * @see MPU6050_RA_CONFIG + * @param sync New FSYNC configuration value + */ +void MPU6050_Base::setExternalFrameSync(uint8_t sync) { + I2Cdev::writeBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_EXT_SYNC_SET_BIT, MPU6050_CFG_EXT_SYNC_SET_LENGTH, sync, wireObj); +} +/** Get digital low-pass filter configuration. + * The DLPF_CFG parameter sets the digital low pass filter configuration. It + * also determines the internal sampling rate used by the device as shown in + * the table below. + * + * Note: The accelerometer output rate is 1kHz. This means that for a Sample + * Rate greater than 1kHz, the same accelerometer sample may be output to the + * FIFO, DMP, and sensor registers more than once. + * + *
+ *          |   ACCELEROMETER    |           GYROSCOPE
+ * DLPF_CFG | Bandwidth | Delay  | Bandwidth | Delay  | Sample Rate
+ * ---------+-----------+--------+-----------+--------+-------------
+ * 0        | 260Hz     | 0ms    | 256Hz     | 0.98ms | 8kHz
+ * 1        | 184Hz     | 2.0ms  | 188Hz     | 1.9ms  | 1kHz
+ * 2        | 94Hz      | 3.0ms  | 98Hz      | 2.8ms  | 1kHz
+ * 3        | 44Hz      | 4.9ms  | 42Hz      | 4.8ms  | 1kHz
+ * 4        | 21Hz      | 8.5ms  | 20Hz      | 8.3ms  | 1kHz
+ * 5        | 10Hz      | 13.8ms | 10Hz      | 13.4ms | 1kHz
+ * 6        | 5Hz       | 19.0ms | 5Hz       | 18.6ms | 1kHz
+ * 7        |   -- Reserved --   |   -- Reserved --   | Reserved
+ * 
+ * + * @return DLFP configuration + * @see MPU6050_RA_CONFIG + * @see MPU6050_CFG_DLPF_CFG_BIT + * @see MPU6050_CFG_DLPF_CFG_LENGTH + */ +uint8_t MPU6050_Base::getDLPFMode() { + I2Cdev::readBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_DLPF_CFG_BIT, MPU6050_CFG_DLPF_CFG_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set digital low-pass filter configuration. + * @param mode New DLFP configuration setting + * @see getDLPFBandwidth() + * @see MPU6050_DLPF_BW_256 + * @see MPU6050_RA_CONFIG + * @see MPU6050_CFG_DLPF_CFG_BIT + * @see MPU6050_CFG_DLPF_CFG_LENGTH + */ +void MPU6050_Base::setDLPFMode(uint8_t mode) { + I2Cdev::writeBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_DLPF_CFG_BIT, MPU6050_CFG_DLPF_CFG_LENGTH, mode, wireObj); +} + +// GYRO_CONFIG register + +/** Get full-scale gyroscope range. + * The FS_SEL parameter allows setting the full-scale range of the gyro sensors, + * as described in the table below. + * + *
+ * 0 = +/- 250 degrees/sec
+ * 1 = +/- 500 degrees/sec
+ * 2 = +/- 1000 degrees/sec
+ * 3 = +/- 2000 degrees/sec
+ * 
+ * + * @return Current full-scale gyroscope range setting + * @see MPU6050_GYRO_FS_250 + * @see MPU6050_RA_GYRO_CONFIG + * @see MPU6050_GCONFIG_FS_SEL_BIT + * @see MPU6050_GCONFIG_FS_SEL_LENGTH + */ +uint8_t MPU6050_Base::getFullScaleGyroRange() { + I2Cdev::readBits(devAddr, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set full-scale gyroscope range. + * @param range New full-scale gyroscope range value + * @see getFullScaleRange() + * @see MPU6050_GYRO_FS_250 + * @see MPU6050_RA_GYRO_CONFIG + * @see MPU6050_GCONFIG_FS_SEL_BIT + * @see MPU6050_GCONFIG_FS_SEL_LENGTH + */ +void MPU6050_Base::setFullScaleGyroRange(uint8_t range) { + I2Cdev::writeBits(devAddr, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, range, wireObj); +} + +// SELF TEST FACTORY TRIM VALUES + +/** Get self-test factory trim value for accelerometer X axis. + * @return factory trim value + * @see MPU6050_RA_SELF_TEST_X + */ +uint8_t MPU6050_Base::getAccelXSelfTestFactoryTrim() { + I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_X, &buffer[0], I2Cdev::readTimeout, wireObj); + I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_A, &buffer[1], I2Cdev::readTimeout, wireObj); + return (buffer[0]>>3) | ((buffer[1]>>4) & 0x03); +} + +/** Get self-test factory trim value for accelerometer Y axis. + * @return factory trim value + * @see MPU6050_RA_SELF_TEST_Y + */ +uint8_t MPU6050_Base::getAccelYSelfTestFactoryTrim() { + I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_Y, &buffer[0], I2Cdev::readTimeout, wireObj); + I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_A, &buffer[1], I2Cdev::readTimeout, wireObj); + return (buffer[0]>>3) | ((buffer[1]>>2) & 0x03); +} + +/** Get self-test factory trim value for accelerometer Z axis. + * @return factory trim value + * @see MPU6050_RA_SELF_TEST_Z + */ +uint8_t MPU6050_Base::getAccelZSelfTestFactoryTrim() { + I2Cdev::readBytes(devAddr, MPU6050_RA_SELF_TEST_Z, 2, buffer, I2Cdev::readTimeout, wireObj); + return (buffer[0]>>3) | (buffer[1] & 0x03); +} + +/** Get self-test factory trim value for gyro X axis. + * @return factory trim value + * @see MPU6050_RA_SELF_TEST_X + */ +uint8_t MPU6050_Base::getGyroXSelfTestFactoryTrim() { + I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_X, buffer, I2Cdev::readTimeout, wireObj); + return (buffer[0] & 0x1F); +} + +/** Get self-test factory trim value for gyro Y axis. + * @return factory trim value + * @see MPU6050_RA_SELF_TEST_Y + */ +uint8_t MPU6050_Base::getGyroYSelfTestFactoryTrim() { + I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_Y, buffer, I2Cdev::readTimeout, wireObj); + return (buffer[0] & 0x1F); +} + +/** Get self-test factory trim value for gyro Z axis. + * @return factory trim value + * @see MPU6050_RA_SELF_TEST_Z + */ +uint8_t MPU6050_Base::getGyroZSelfTestFactoryTrim() { + I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_Z, buffer, I2Cdev::readTimeout, wireObj); + return (buffer[0] & 0x1F); +} + +// ACCEL_CONFIG register + +/** Get self-test enabled setting for accelerometer X axis. + * @return Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +bool MPU6050_Base::getAccelXSelfTest() { + I2Cdev::readBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_XA_ST_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get self-test enabled setting for accelerometer X axis. + * @param enabled Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050_Base::setAccelXSelfTest(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_XA_ST_BIT, enabled, wireObj); +} +/** Get self-test enabled value for accelerometer Y axis. + * @return Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +bool MPU6050_Base::getAccelYSelfTest() { + I2Cdev::readBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_YA_ST_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get self-test enabled value for accelerometer Y axis. + * @param enabled Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050_Base::setAccelYSelfTest(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_YA_ST_BIT, enabled, wireObj); +} +/** Get self-test enabled value for accelerometer Z axis. + * @return Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +bool MPU6050_Base::getAccelZSelfTest() { + I2Cdev::readBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ZA_ST_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set self-test enabled value for accelerometer Z axis. + * @param enabled Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050_Base::setAccelZSelfTest(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ZA_ST_BIT, enabled, wireObj); +} +/** Get full-scale accelerometer range. + * The FS_SEL parameter allows setting the full-scale range of the accelerometer + * sensors, as described in the table below. + * + *
+ * 0 = +/- 2g
+ * 1 = +/- 4g
+ * 2 = +/- 8g
+ * 3 = +/- 16g
+ * 
+ * + * @return Current full-scale accelerometer range setting + * @see MPU6050_ACCEL_FS_2 + * @see MPU6050_RA_ACCEL_CONFIG + * @see MPU6050_ACONFIG_AFS_SEL_BIT + * @see MPU6050_ACONFIG_AFS_SEL_LENGTH + */ +uint8_t MPU6050_Base::getFullScaleAccelRange() { + I2Cdev::readBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set full-scale accelerometer range. + * @param range New full-scale accelerometer range setting + * @see getFullScaleAccelRange() + */ +void MPU6050_Base::setFullScaleAccelRange(uint8_t range) { + I2Cdev::writeBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, range, wireObj); +} +/** Get the high-pass filter configuration. + * The DHPF is a filter module in the path leading to motion detectors (Free + * Fall, Motion threshold, and Zero Motion). The high pass filter output is not + * available to the data registers (see Figure in Section 8 of the MPU-6000/ + * MPU-6050 Product Specification document). + * + * The high pass filter has three modes: + * + *
+ *    Reset: The filter output settles to zero within one sample. This
+ *           effectively disables the high pass filter. This mode may be toggled
+ *           to quickly settle the filter.
+ *
+ *    On:    The high pass filter will pass signals above the cut off frequency.
+ *
+ *    Hold:  When triggered, the filter holds the present sample. The filter
+ *           output will be the difference between the input sample and the held
+ *           sample.
+ * 
+ * + *
+ * ACCEL_HPF | Filter Mode | Cut-off Frequency
+ * ----------+-------------+------------------
+ * 0         | Reset       | None
+ * 1         | On          | 5Hz
+ * 2         | On          | 2.5Hz
+ * 3         | On          | 1.25Hz
+ * 4         | On          | 0.63Hz
+ * 7         | Hold        | None
+ * 
+ * + * @return Current high-pass filter configuration + * @see MPU6050_DHPF_RESET + * @see MPU6050_RA_ACCEL_CONFIG + */ +uint8_t MPU6050_Base::getDHPFMode() { + I2Cdev::readBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the high-pass filter configuration. + * @param bandwidth New high-pass filter configuration + * @see setDHPFMode() + * @see MPU6050_DHPF_RESET + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050_Base::setDHPFMode(uint8_t bandwidth) { + I2Cdev::writeBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_LENGTH, bandwidth, wireObj); +} + +// FF_THR register + +/** Get free-fall event acceleration threshold. + * This register configures the detection threshold for Free Fall event + * detection. The unit of FF_THR is 1LSB = 2mg. Free Fall is detected when the + * absolute value of the accelerometer measurements for the three axes are each + * less than the detection threshold. This condition increments the Free Fall + * duration counter (Register 30). The Free Fall interrupt is triggered when the + * Free Fall duration counter reaches the time specified in FF_DUR. + * + * For more details on the Free Fall detection interrupt, see Section 8.2 of the + * MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and + * 58 of this document. + * + * @return Current free-fall acceleration threshold value (LSB = 2mg) + * @see MPU6050_RA_FF_THR + */ +uint8_t MPU6050_Base::getFreefallDetectionThreshold() { + I2Cdev::readByte(devAddr, MPU6050_RA_FF_THR, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get free-fall event acceleration threshold. + * @param threshold New free-fall acceleration threshold value (LSB = 2mg) + * @see getFreefallDetectionThreshold() + * @see MPU6050_RA_FF_THR + */ +void MPU6050_Base::setFreefallDetectionThreshold(uint8_t threshold) { + I2Cdev::writeByte(devAddr, MPU6050_RA_FF_THR, threshold, wireObj); +} + +// FF_DUR register + +/** Get free-fall event duration threshold. + * This register configures the duration counter threshold for Free Fall event + * detection. The duration counter ticks at 1kHz, therefore FF_DUR has a unit + * of 1 LSB = 1 ms. + * + * The Free Fall duration counter increments while the absolute value of the + * accelerometer measurements are each less than the detection threshold + * (Register 29). The Free Fall interrupt is triggered when the Free Fall + * duration counter reaches the time specified in this register. + * + * For more details on the Free Fall detection interrupt, see Section 8.2 of + * the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 + * and 58 of this document. + * + * @return Current free-fall duration threshold value (LSB = 1ms) + * @see MPU6050_RA_FF_DUR + */ +uint8_t MPU6050_Base::getFreefallDetectionDuration() { + I2Cdev::readByte(devAddr, MPU6050_RA_FF_DUR, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get free-fall event duration threshold. + * @param duration New free-fall duration threshold value (LSB = 1ms) + * @see getFreefallDetectionDuration() + * @see MPU6050_RA_FF_DUR + */ +void MPU6050_Base::setFreefallDetectionDuration(uint8_t duration) { + I2Cdev::writeByte(devAddr, MPU6050_RA_FF_DUR, duration, wireObj); +} + +// MOT_THR register + +/** Get motion detection event acceleration threshold. + * This register configures the detection threshold for Motion interrupt + * generation. The unit of MOT_THR is 1LSB = 2mg. Motion is detected when the + * absolute value of any of the accelerometer measurements exceeds this Motion + * detection threshold. This condition increments the Motion detection duration + * counter (Register 32). The Motion detection interrupt is triggered when the + * Motion Detection counter reaches the time count specified in MOT_DUR + * (Register 32). + * + * The Motion interrupt will indicate the axis and polarity of detected motion + * in MOT_DETECT_STATUS (Register 97). + * + * For more details on the Motion detection interrupt, see Section 8.3 of the + * MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and + * 58 of this document. + * + * @return Current motion detection acceleration threshold value (LSB = 2mg) + * @see MPU6050_RA_MOT_THR + */ +uint8_t MPU6050_Base::getMotionDetectionThreshold() { + I2Cdev::readByte(devAddr, MPU6050_RA_MOT_THR, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set motion detection event acceleration threshold. + * @param threshold New motion detection acceleration threshold value (LSB = 2mg) + * @see getMotionDetectionThreshold() + * @see MPU6050_RA_MOT_THR + */ +void MPU6050_Base::setMotionDetectionThreshold(uint8_t threshold) { + I2Cdev::writeByte(devAddr, MPU6050_RA_MOT_THR, threshold, wireObj); +} + +// MOT_DUR register + +/** Get motion detection event duration threshold. + * This register configures the duration counter threshold for Motion interrupt + * generation. The duration counter ticks at 1 kHz, therefore MOT_DUR has a unit + * of 1LSB = 1ms. The Motion detection duration counter increments when the + * absolute value of any of the accelerometer measurements exceeds the Motion + * detection threshold (Register 31). The Motion detection interrupt is + * triggered when the Motion detection counter reaches the time count specified + * in this register. + * + * For more details on the Motion detection interrupt, see Section 8.3 of the + * MPU-6000/MPU-6050 Product Specification document. + * + * @return Current motion detection duration threshold value (LSB = 1ms) + * @see MPU6050_RA_MOT_DUR + */ +uint8_t MPU6050_Base::getMotionDetectionDuration() { + I2Cdev::readByte(devAddr, MPU6050_RA_MOT_DUR, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set motion detection event duration threshold. + * @param duration New motion detection duration threshold value (LSB = 1ms) + * @see getMotionDetectionDuration() + * @see MPU6050_RA_MOT_DUR + */ +void MPU6050_Base::setMotionDetectionDuration(uint8_t duration) { + I2Cdev::writeByte(devAddr, MPU6050_RA_MOT_DUR, duration, wireObj); +} + +// ZRMOT_THR register + +/** Get zero motion detection event acceleration threshold. + * This register configures the detection threshold for Zero Motion interrupt + * generation. The unit of ZRMOT_THR is 1LSB = 2mg. Zero Motion is detected when + * the absolute value of the accelerometer measurements for the 3 axes are each + * less than the detection threshold. This condition increments the Zero Motion + * duration counter (Register 34). The Zero Motion interrupt is triggered when + * the Zero Motion duration counter reaches the time count specified in + * ZRMOT_DUR (Register 34). + * + * Unlike Free Fall or Motion detection, Zero Motion detection triggers an + * interrupt both when Zero Motion is first detected and when Zero Motion is no + * longer detected. + * + * When a zero motion event is detected, a Zero Motion Status will be indicated + * in the MOT_DETECT_STATUS register (Register 97). When a motion-to-zero-motion + * condition is detected, the status bit is set to 1. When a zero-motion-to- + * motion condition is detected, the status bit is set to 0. + * + * For more details on the Zero Motion detection interrupt, see Section 8.4 of + * the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 + * and 58 of this document. + * + * @return Current zero motion detection acceleration threshold value (LSB = 2mg) + * @see MPU6050_RA_ZRMOT_THR + */ +uint8_t MPU6050_Base::getZeroMotionDetectionThreshold() { + I2Cdev::readByte(devAddr, MPU6050_RA_ZRMOT_THR, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set zero motion detection event acceleration threshold. + * @param threshold New zero motion detection acceleration threshold value (LSB = 2mg) + * @see getZeroMotionDetectionThreshold() + * @see MPU6050_RA_ZRMOT_THR + */ +void MPU6050_Base::setZeroMotionDetectionThreshold(uint8_t threshold) { + I2Cdev::writeByte(devAddr, MPU6050_RA_ZRMOT_THR, threshold, wireObj); +} + +// ZRMOT_DUR register + +/** Get zero motion detection event duration threshold. + * This register configures the duration counter threshold for Zero Motion + * interrupt generation. The duration counter ticks at 16 Hz, therefore + * ZRMOT_DUR has a unit of 1 LSB = 64 ms. The Zero Motion duration counter + * increments while the absolute value of the accelerometer measurements are + * each less than the detection threshold (Register 33). The Zero Motion + * interrupt is triggered when the Zero Motion duration counter reaches the time + * count specified in this register. + * + * For more details on the Zero Motion detection interrupt, see Section 8.4 of + * the MPU-6000/MPU-6050 Product Specification document, as well as Registers 56 + * and 58 of this document. + * + * @return Current zero motion detection duration threshold value (LSB = 64ms) + * @see MPU6050_RA_ZRMOT_DUR + */ +uint8_t MPU6050_Base::getZeroMotionDetectionDuration() { + I2Cdev::readByte(devAddr, MPU6050_RA_ZRMOT_DUR, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set zero motion detection event duration threshold. + * @param duration New zero motion detection duration threshold value (LSB = 1ms) + * @see getZeroMotionDetectionDuration() + * @see MPU6050_RA_ZRMOT_DUR + */ +void MPU6050_Base::setZeroMotionDetectionDuration(uint8_t duration) { + I2Cdev::writeByte(devAddr, MPU6050_RA_ZRMOT_DUR, duration, wireObj); +} + +// FIFO_EN register + +/** Get temperature FIFO enabled value. + * When set to 1, this bit enables TEMP_OUT_H and TEMP_OUT_L (Registers 65 and + * 66) to be written into the FIFO buffer. + * @return Current temperature FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getTempFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_TEMP_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set temperature FIFO enabled value. + * @param enabled New temperature FIFO enabled value + * @see getTempFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setTempFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_TEMP_FIFO_EN_BIT, enabled, wireObj); +} +/** Get gyroscope X-axis FIFO enabled value. + * When set to 1, this bit enables GYRO_XOUT_H and GYRO_XOUT_L (Registers 67 and + * 68) to be written into the FIFO buffer. + * @return Current gyroscope X-axis FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getXGyroFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_XG_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set gyroscope X-axis FIFO enabled value. + * @param enabled New gyroscope X-axis FIFO enabled value + * @see getXGyroFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setXGyroFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_XG_FIFO_EN_BIT, enabled, wireObj); +} +/** Get gyroscope Y-axis FIFO enabled value. + * When set to 1, this bit enables GYRO_YOUT_H and GYRO_YOUT_L (Registers 69 and + * 70) to be written into the FIFO buffer. + * @return Current gyroscope Y-axis FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getYGyroFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_YG_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set gyroscope Y-axis FIFO enabled value. + * @param enabled New gyroscope Y-axis FIFO enabled value + * @see getYGyroFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setYGyroFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_YG_FIFO_EN_BIT, enabled, wireObj); +} +/** Get gyroscope Z-axis FIFO enabled value. + * When set to 1, this bit enables GYRO_ZOUT_H and GYRO_ZOUT_L (Registers 71 and + * 72) to be written into the FIFO buffer. + * @return Current gyroscope Z-axis FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getZGyroFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ZG_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set gyroscope Z-axis FIFO enabled value. + * @param enabled New gyroscope Z-axis FIFO enabled value + * @see getZGyroFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setZGyroFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ZG_FIFO_EN_BIT, enabled, wireObj); +} +/** Get accelerometer FIFO enabled value. + * When set to 1, this bit enables ACCEL_XOUT_H, ACCEL_XOUT_L, ACCEL_YOUT_H, + * ACCEL_YOUT_L, ACCEL_ZOUT_H, and ACCEL_ZOUT_L (Registers 59 to 64) to be + * written into the FIFO buffer. + * @return Current accelerometer FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getAccelFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ACCEL_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set accelerometer FIFO enabled value. + * @param enabled New accelerometer FIFO enabled value + * @see getAccelFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setAccelFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ACCEL_FIFO_EN_BIT, enabled, wireObj); +} +/** Get Slave 2 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 2 to be written into the FIFO buffer. + * @return Current Slave 2 FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getSlave2FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV2_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Slave 2 FIFO enabled value. + * @param enabled New Slave 2 FIFO enabled value + * @see getSlave2FIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setSlave2FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV2_FIFO_EN_BIT, enabled, wireObj); +} +/** Get Slave 1 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 1 to be written into the FIFO buffer. + * @return Current Slave 1 FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getSlave1FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV1_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Slave 1 FIFO enabled value. + * @param enabled New Slave 1 FIFO enabled value + * @see getSlave1FIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setSlave1FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV1_FIFO_EN_BIT, enabled, wireObj); +} +/** Get Slave 0 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 0 to be written into the FIFO buffer. + * @return Current Slave 0 FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050_Base::getSlave0FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV0_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Slave 0 FIFO enabled value. + * @param enabled New Slave 0 FIFO enabled value + * @see getSlave0FIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050_Base::setSlave0FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV0_FIFO_EN_BIT, enabled, wireObj); +} + +// I2C_MST_CTRL register + +/** Get multi-master enabled value. + * Multi-master capability allows multiple I2C masters to operate on the same + * bus. In circuits where multi-master capability is required, set MULT_MST_EN + * to 1. This will increase current drawn by approximately 30uA. + * + * In circuits where multi-master capability is required, the state of the I2C + * bus must always be monitored by each separate I2C Master. Before an I2C + * Master can assume arbitration of the bus, it must first confirm that no other + * I2C Master has arbitration of the bus. When MULT_MST_EN is set to 1, the + * MPU-60X0's bus arbitration detection logic is turned on, enabling it to + * detect when the bus is available. + * + * @return Current multi-master enabled value + * @see MPU6050_RA_I2C_MST_CTRL + */ +bool MPU6050_Base::getMultiMasterEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_MULT_MST_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set multi-master enabled value. + * @param enabled New multi-master enabled value + * @see getMultiMasterEnabled() + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050_Base::setMultiMasterEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_MULT_MST_EN_BIT, enabled, wireObj); +} +/** Get wait-for-external-sensor-data enabled value. + * When the WAIT_FOR_ES bit is set to 1, the Data Ready interrupt will be + * delayed until External Sensor data from the Slave Devices are loaded into the + * EXT_SENS_DATA registers. This is used to ensure that both the internal sensor + * data (i.e. from gyro and accel) and external sensor data have been loaded to + * their respective data registers (i.e. the data is synced) when the Data Ready + * interrupt is triggered. + * + * @return Current wait-for-external-sensor-data enabled value + * @see MPU6050_RA_I2C_MST_CTRL + */ +bool MPU6050_Base::getWaitForExternalSensorEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_WAIT_FOR_ES_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set wait-for-external-sensor-data enabled value. + * @param enabled New wait-for-external-sensor-data enabled value + * @see getWaitForExternalSensorEnabled() + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050_Base::setWaitForExternalSensorEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_WAIT_FOR_ES_BIT, enabled, wireObj); +} +/** Get Slave 3 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 3 to be written into the FIFO buffer. + * @return Current Slave 3 FIFO enabled value + * @see MPU6050_RA_MST_CTRL + */ +bool MPU6050_Base::getSlave3FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_SLV_3_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Slave 3 FIFO enabled value. + * @param enabled New Slave 3 FIFO enabled value + * @see getSlave3FIFOEnabled() + * @see MPU6050_RA_MST_CTRL + */ +void MPU6050_Base::setSlave3FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_SLV_3_FIFO_EN_BIT, enabled, wireObj); +} +/** Get slave read/write transition enabled value. + * The I2C_MST_P_NSR bit configures the I2C Master's transition from one slave + * read to the next slave read. If the bit equals 0, there will be a restart + * between reads. If the bit equals 1, there will be a stop followed by a start + * of the following read. When a write transaction follows a read transaction, + * the stop followed by a start of the successive write will be always used. + * + * @return Current slave read/write transition enabled value + * @see MPU6050_RA_I2C_MST_CTRL + */ +bool MPU6050_Base::getSlaveReadWriteTransitionEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_P_NSR_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set slave read/write transition enabled value. + * @param enabled New slave read/write transition enabled value + * @see getSlaveReadWriteTransitionEnabled() + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050_Base::setSlaveReadWriteTransitionEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_P_NSR_BIT, enabled, wireObj); +} +/** Get I2C master clock speed. + * I2C_MST_CLK is a 4 bit unsigned value which configures a divider on the + * MPU-60X0 internal 8MHz clock. It sets the I2C master clock speed according to + * the following table: + * + *
+ * I2C_MST_CLK | I2C Master Clock Speed | 8MHz Clock Divider
+ * ------------+------------------------+-------------------
+ * 0           | 348kHz                 | 23
+ * 1           | 333kHz                 | 24
+ * 2           | 320kHz                 | 25
+ * 3           | 308kHz                 | 26
+ * 4           | 296kHz                 | 27
+ * 5           | 286kHz                 | 28
+ * 6           | 276kHz                 | 29
+ * 7           | 267kHz                 | 30
+ * 8           | 258kHz                 | 31
+ * 9           | 500kHz                 | 16
+ * 10          | 471kHz                 | 17
+ * 11          | 444kHz                 | 18
+ * 12          | 421kHz                 | 19
+ * 13          | 400kHz                 | 20
+ * 14          | 381kHz                 | 21
+ * 15          | 364kHz                 | 22
+ * 
+ * + * @return Current I2C master clock speed + * @see MPU6050_RA_I2C_MST_CTRL + */ +uint8_t MPU6050_Base::getMasterClockSpeed() { + I2Cdev::readBits(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set I2C master clock speed. + * @reparam speed Current I2C master clock speed + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050_Base::setMasterClockSpeed(uint8_t speed) { + I2Cdev::writeBits(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_LENGTH, speed, wireObj); +} + +// I2C_SLV* registers (Slave 0-3) + +/** Get the I2C address of the specified slave (0-3). + * Note that Bit 7 (MSB) controls read/write mode. If Bit 7 is set, it's a read + * operation, and if it is cleared, then it's a write operation. The remaining + * bits (6-0) are the 7-bit device address of the slave device. + * + * In read mode, the result of the read is placed in the lowest available + * EXT_SENS_DATA register. For further information regarding the allocation of + * read results, please refer to the EXT_SENS_DATA register description + * (Registers 73 - 96). + * + * The MPU-6050 supports a total of five slaves, but Slave 4 has unique + * characteristics, and so it has its own functions (getSlave4* and setSlave4*). + * + * I2C data transactions are performed at the Sample Rate, as defined in + * Register 25. The user is responsible for ensuring that I2C data transactions + * to and from each enabled Slave can be completed within a single period of the + * Sample Rate. + * + * The I2C slave access rate can be reduced relative to the Sample Rate. This + * reduced access rate is determined by I2C_MST_DLY (Register 52). Whether a + * slave's access rate is reduced relative to the Sample Rate is determined by + * I2C_MST_DELAY_CTRL (Register 103). + * + * The processing order for the slaves is fixed. The sequence followed for + * processing the slaves is Slave 0, Slave 1, Slave 2, Slave 3 and Slave 4. If a + * particular Slave is disabled it will be skipped. + * + * Each slave can either be accessed at the sample rate or at a reduced sample + * rate. In a case where some slaves are accessed at the Sample Rate and some + * slaves are accessed at the reduced rate, the sequence of accessing the slaves + * (Slave 0 to Slave 4) is still followed. However, the reduced rate slaves will + * be skipped if their access rate dictates that they should not be accessed + * during that particular cycle. For further information regarding the reduced + * access rate, please refer to Register 52. Whether a slave is accessed at the + * Sample Rate or at the reduced rate is determined by the Delay Enable bits in + * Register 103. + * + * @param num Slave number (0-3) + * @return Current address for specified slave + * @see MPU6050_RA_I2C_SLV0_ADDR + */ +uint8_t MPU6050_Base::getSlaveAddress(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV0_ADDR + num*3, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the I2C address of the specified slave (0-3). + * @param num Slave number (0-3) + * @param address New address for specified slave + * @see getSlaveAddress() + * @see MPU6050_RA_I2C_SLV0_ADDR + */ +void MPU6050_Base::setSlaveAddress(uint8_t num, uint8_t address) { + if (num > 3) return; + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV0_ADDR + num*3, address, wireObj); +} +/** Get the active internal register for the specified slave (0-3). + * Read/write operations for this slave will be done to whatever internal + * register address is stored in this MPU register. + * + * The MPU-6050 supports a total of five slaves, but Slave 4 has unique + * characteristics, and so it has its own functions. + * + * @param num Slave number (0-3) + * @return Current active register for specified slave + * @see MPU6050_RA_I2C_SLV0_REG + */ +uint8_t MPU6050_Base::getSlaveRegister(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV0_REG + num*3, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the active internal register for the specified slave (0-3). + * @param num Slave number (0-3) + * @param reg New active register for specified slave + * @see getSlaveRegister() + * @see MPU6050_RA_I2C_SLV0_REG + */ +void MPU6050_Base::setSlaveRegister(uint8_t num, uint8_t reg) { + if (num > 3) return; + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV0_REG + num*3, reg, wireObj); +} +/** Get the enabled value for the specified slave (0-3). + * When set to 1, this bit enables Slave 0 for data transfer operations. When + * cleared to 0, this bit disables Slave 0 from data transfer operations. + * @param num Slave number (0-3) + * @return Current enabled value for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050_Base::getSlaveEnabled(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the enabled value for the specified slave (0-3). + * @param num Slave number (0-3) + * @param enabled New enabled value for specified slave + * @see getSlaveEnabled() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050_Base::setSlaveEnabled(uint8_t num, bool enabled) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_EN_BIT, enabled, wireObj); +} +/** Get word pair byte-swapping enabled for the specified slave (0-3). + * When set to 1, this bit enables byte swapping. When byte swapping is enabled, + * the high and low bytes of a word pair are swapped. Please refer to + * I2C_SLV0_GRP for the pairing convention of the word pairs. When cleared to 0, + * bytes transferred to and from Slave 0 will be written to EXT_SENS_DATA + * registers in the order they were transferred. + * + * @param num Slave number (0-3) + * @return Current word pair byte-swapping enabled value for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050_Base::getSlaveWordByteSwap(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_BYTE_SW_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set word pair byte-swapping enabled for the specified slave (0-3). + * @param num Slave number (0-3) + * @param enabled New word pair byte-swapping enabled value for specified slave + * @see getSlaveWordByteSwap() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050_Base::setSlaveWordByteSwap(uint8_t num, bool enabled) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_BYTE_SW_BIT, enabled, wireObj); +} +/** Get write mode for the specified slave (0-3). + * When set to 1, the transaction will read or write data only. When cleared to + * 0, the transaction will write a register address prior to reading or writing + * data. This should equal 0 when specifying the register address within the + * Slave device to/from which the ensuing data transaction will take place. + * + * @param num Slave number (0-3) + * @return Current write mode for specified slave (0 = register address + data, 1 = data only) + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050_Base::getSlaveWriteMode(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_REG_DIS_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set write mode for the specified slave (0-3). + * @param num Slave number (0-3) + * @param mode New write mode for specified slave (0 = register address + data, 1 = data only) + * @see getSlaveWriteMode() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050_Base::setSlaveWriteMode(uint8_t num, bool mode) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_REG_DIS_BIT, mode, wireObj); +} +/** Get word pair grouping order offset for the specified slave (0-3). + * This sets specifies the grouping order of word pairs received from registers. + * When cleared to 0, bytes from register addresses 0 and 1, 2 and 3, etc (even, + * then odd register addresses) are paired to form a word. When set to 1, bytes + * from register addresses are paired 1 and 2, 3 and 4, etc. (odd, then even + * register addresses) are paired to form a word. + * + * @param num Slave number (0-3) + * @return Current word pair grouping order offset for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050_Base::getSlaveWordGroupOffset(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_GRP_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set word pair grouping order offset for the specified slave (0-3). + * @param num Slave number (0-3) + * @param enabled New word pair grouping order offset for specified slave + * @see getSlaveWordGroupOffset() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050_Base::setSlaveWordGroupOffset(uint8_t num, bool enabled) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_GRP_BIT, enabled, wireObj); +} +/** Get number of bytes to read for the specified slave (0-3). + * Specifies the number of bytes transferred to and from Slave 0. Clearing this + * bit to 0 is equivalent to disabling the register by writing 0 to I2C_SLV0_EN. + * @param num Slave number (0-3) + * @return Number of bytes to read for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +uint8_t MPU6050_Base::getSlaveDataLength(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBits(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_LEN_BIT, MPU6050_I2C_SLV_LEN_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set number of bytes to read for the specified slave (0-3). + * @param num Slave number (0-3) + * @param length Number of bytes to read for specified slave + * @see getSlaveDataLength() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050_Base::setSlaveDataLength(uint8_t num, uint8_t length) { + if (num > 3) return; + I2Cdev::writeBits(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_LEN_BIT, MPU6050_I2C_SLV_LEN_LENGTH, length, wireObj); +} + +// I2C_SLV* registers (Slave 4) + +/** Get the I2C address of Slave 4. + * Note that Bit 7 (MSB) controls read/write mode. If Bit 7 is set, it's a read + * operation, and if it is cleared, then it's a write operation. The remaining + * bits (6-0) are the 7-bit device address of the slave device. + * + * @return Current address for Slave 4 + * @see getSlaveAddress() + * @see MPU6050_RA_I2C_SLV4_ADDR + */ +uint8_t MPU6050_Base::getSlave4Address() { + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV4_ADDR, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the I2C address of Slave 4. + * @param address New address for Slave 4 + * @see getSlave4Address() + * @see MPU6050_RA_I2C_SLV4_ADDR + */ +void MPU6050_Base::setSlave4Address(uint8_t address) { + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV4_ADDR, address, wireObj); +} +/** Get the active internal register for the Slave 4. + * Read/write operations for this slave will be done to whatever internal + * register address is stored in this MPU register. + * + * @return Current active register for Slave 4 + * @see MPU6050_RA_I2C_SLV4_REG + */ +uint8_t MPU6050_Base::getSlave4Register() { + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV4_REG, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the active internal register for Slave 4. + * @param reg New active register for Slave 4 + * @see getSlave4Register() + * @see MPU6050_RA_I2C_SLV4_REG + */ +void MPU6050_Base::setSlave4Register(uint8_t reg) { + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV4_REG, reg, wireObj); +} +/** Set new byte to write to Slave 4. + * This register stores the data to be written into the Slave 4. If I2C_SLV4_RW + * is set 1 (set to read), this register has no effect. + * @param data New byte to write to Slave 4 + * @see MPU6050_RA_I2C_SLV4_DO + */ +void MPU6050_Base::setSlave4OutputByte(uint8_t data) { + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV4_DO, data, wireObj); +} +/** Get the enabled value for the Slave 4. + * When set to 1, this bit enables Slave 4 for data transfer operations. When + * cleared to 0, this bit disables Slave 4 from data transfer operations. + * @return Current enabled value for Slave 4 + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +bool MPU6050_Base::getSlave4Enabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the enabled value for Slave 4. + * @param enabled New enabled value for Slave 4 + * @see getSlave4Enabled() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050_Base::setSlave4Enabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_EN_BIT, enabled, wireObj); +} +/** Get the enabled value for Slave 4 transaction interrupts. + * When set to 1, this bit enables the generation of an interrupt signal upon + * completion of a Slave 4 transaction. When cleared to 0, this bit disables the + * generation of an interrupt signal upon completion of a Slave 4 transaction. + * The interrupt status can be observed in Register 54. + * + * @return Current enabled value for Slave 4 transaction interrupts. + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +bool MPU6050_Base::getSlave4InterruptEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_INT_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set the enabled value for Slave 4 transaction interrupts. + * @param enabled New enabled value for Slave 4 transaction interrupts. + * @see getSlave4InterruptEnabled() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050_Base::setSlave4InterruptEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_INT_EN_BIT, enabled, wireObj); +} +/** Get write mode for Slave 4. + * When set to 1, the transaction will read or write data only. When cleared to + * 0, the transaction will write a register address prior to reading or writing + * data. This should equal 0 when specifying the register address within the + * Slave device to/from which the ensuing data transaction will take place. + * + * @return Current write mode for Slave 4 (0 = register address + data, 1 = data only) + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +bool MPU6050_Base::getSlave4WriteMode() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_REG_DIS_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set write mode for the Slave 4. + * @param mode New write mode for Slave 4 (0 = register address + data, 1 = data only) + * @see getSlave4WriteMode() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050_Base::setSlave4WriteMode(bool mode) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_REG_DIS_BIT, mode, wireObj); +} +/** Get Slave 4 master delay value. + * This configures the reduced access rate of I2C slaves relative to the Sample + * Rate. When a slave's access rate is decreased relative to the Sample Rate, + * the slave is accessed every: + * + * 1 / (1 + I2C_MST_DLY) samples + * + * This base Sample Rate in turn is determined by SMPLRT_DIV (register 25) and + * DLPF_CFG (register 26). Whether a slave's access rate is reduced relative to + * the Sample Rate is determined by I2C_MST_DELAY_CTRL (register 103). For + * further information regarding the Sample Rate, please refer to register 25. + * + * @return Current Slave 4 master delay value + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +uint8_t MPU6050_Base::getSlave4MasterDelay() { + I2Cdev::readBits(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_MST_DLY_BIT, MPU6050_I2C_SLV4_MST_DLY_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Slave 4 master delay value. + * @param delay New Slave 4 master delay value + * @see getSlave4MasterDelay() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050_Base::setSlave4MasterDelay(uint8_t delay) { + I2Cdev::writeBits(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_MST_DLY_BIT, MPU6050_I2C_SLV4_MST_DLY_LENGTH, delay, wireObj); +} +/** Get last available byte read from Slave 4. + * This register stores the data read from Slave 4. This field is populated + * after a read transaction. + * @return Last available byte read from to Slave 4 + * @see MPU6050_RA_I2C_SLV4_DI + */ +uint8_t MPU6050_Base::getSlate4InputByte() { + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV4_DI, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} + +// I2C_MST_STATUS register + +/** Get FSYNC interrupt status. + * This bit reflects the status of the FSYNC interrupt from an external device + * into the MPU-60X0. This is used as a way to pass an external interrupt + * through the MPU-60X0 to the host application processor. When set to 1, this + * bit will cause an interrupt if FSYNC_INT_EN is asserted in INT_PIN_CFG + * (Register 55). + * @return FSYNC interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getPassthroughStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_PASS_THROUGH_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Slave 4 transaction done status. + * Automatically sets to 1 when a Slave 4 transaction has completed. This + * triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register + * (Register 56) is asserted and if the SLV_4_DONE_INT bit is asserted in the + * I2C_SLV4_CTRL register (Register 52). + * @return Slave 4 transaction done status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getSlave4IsDone() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV4_DONE_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get master arbitration lost status. + * This bit automatically sets to 1 when the I2C Master has lost arbitration of + * the auxiliary I2C bus (an error condition). This triggers an interrupt if the + * I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted. + * @return Master arbitration lost status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getLostArbitration() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_LOST_ARB_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Slave 4 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 4. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 4 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getSlave4Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV4_NACK_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Slave 3 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 3. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 3 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getSlave3Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV3_NACK_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Slave 2 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 2. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 2 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getSlave2Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV2_NACK_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Slave 1 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 1. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 1 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getSlave1Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV1_NACK_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Slave 0 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 0. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 0 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050_Base::getSlave0Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV0_NACK_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} + +// INT_PIN_CFG register + +/** Get interrupt logic level mode. + * Will be set 0 for active-high, 1 for active-low. + * @return Current interrupt mode (0=active-high, 1=active-low) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_LEVEL_BIT + */ +bool MPU6050_Base::getInterruptMode() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_LEVEL_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set interrupt logic level mode. + * @param mode New interrupt mode (0=active-high, 1=active-low) + * @see getInterruptMode() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_LEVEL_BIT + */ +void MPU6050_Base::setInterruptMode(bool mode) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_LEVEL_BIT, mode, wireObj); +} +/** Get interrupt drive mode. + * Will be set 0 for push-pull, 1 for open-drain. + * @return Current interrupt drive mode (0=push-pull, 1=open-drain) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_OPEN_BIT + */ +bool MPU6050_Base::getInterruptDrive() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_OPEN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set interrupt drive mode. + * @param drive New interrupt drive mode (0=push-pull, 1=open-drain) + * @see getInterruptDrive() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_OPEN_BIT + */ +void MPU6050_Base::setInterruptDrive(bool drive) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_OPEN_BIT, drive, wireObj); +} +/** Get interrupt latch mode. + * Will be set 0 for 50us-pulse, 1 for latch-until-int-cleared. + * @return Current latch mode (0=50us-pulse, 1=latch-until-int-cleared) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_LATCH_INT_EN_BIT + */ +bool MPU6050_Base::getInterruptLatch() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_LATCH_INT_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set interrupt latch mode. + * @param latch New latch mode (0=50us-pulse, 1=latch-until-int-cleared) + * @see getInterruptLatch() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_LATCH_INT_EN_BIT + */ +void MPU6050_Base::setInterruptLatch(bool latch) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_LATCH_INT_EN_BIT, latch, wireObj); +} +/** Get interrupt latch clear mode. + * Will be set 0 for status-read-only, 1 for any-register-read. + * @return Current latch clear mode (0=status-read-only, 1=any-register-read) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_RD_CLEAR_BIT + */ +bool MPU6050_Base::getInterruptLatchClear() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_RD_CLEAR_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set interrupt latch clear mode. + * @param clear New latch clear mode (0=status-read-only, 1=any-register-read) + * @see getInterruptLatchClear() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_RD_CLEAR_BIT + */ +void MPU6050_Base::setInterruptLatchClear(bool clear) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_RD_CLEAR_BIT, clear, wireObj); +} +/** Get FSYNC interrupt logic level mode. + * @return Current FSYNC interrupt mode (0=active-high, 1=active-low) + * @see getFSyncInterruptMode() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT + */ +bool MPU6050_Base::getFSyncInterruptLevel() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set FSYNC interrupt logic level mode. + * @param mode New FSYNC interrupt mode (0=active-high, 1=active-low) + * @see getFSyncInterruptMode() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT + */ +void MPU6050_Base::setFSyncInterruptLevel(bool level) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT, level, wireObj); +} +/** Get FSYNC pin interrupt enabled setting. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled setting + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_EN_BIT + */ +bool MPU6050_Base::getFSyncInterruptEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set FSYNC pin interrupt enabled setting. + * @param enabled New FSYNC pin interrupt enabled setting + * @see getFSyncInterruptEnabled() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_EN_BIT + */ +void MPU6050_Base::setFSyncInterruptEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_EN_BIT, enabled, wireObj); +} +/** Get I2C bypass enabled status. + * When this bit is equal to 1 and I2C_MST_EN (Register 106 bit[5]) is equal to + * 0, the host application processor will be able to directly access the + * auxiliary I2C bus of the MPU-60X0. When this bit is equal to 0, the host + * application processor will not be able to directly access the auxiliary I2C + * bus of the MPU-60X0 regardless of the state of I2C_MST_EN (Register 106 + * bit[5]). + * @return Current I2C bypass enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_I2C_BYPASS_EN_BIT + */ +bool MPU6050_Base::getI2CBypassEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set I2C bypass enabled status. + * When this bit is equal to 1 and I2C_MST_EN (Register 106 bit[5]) is equal to + * 0, the host application processor will be able to directly access the + * auxiliary I2C bus of the MPU-60X0. When this bit is equal to 0, the host + * application processor will not be able to directly access the auxiliary I2C + * bus of the MPU-60X0 regardless of the state of I2C_MST_EN (Register 106 + * bit[5]). + * @param enabled New I2C bypass enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_I2C_BYPASS_EN_BIT + */ +void MPU6050_Base::setI2CBypassEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT, enabled, wireObj); +} +/** Get reference clock output enabled status. + * When this bit is equal to 1, a reference clock output is provided at the + * CLKOUT pin. When this bit is equal to 0, the clock output is disabled. For + * further information regarding CLKOUT, please refer to the MPU-60X0 Product + * Specification document. + * @return Current reference clock output enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_CLKOUT_EN_BIT + */ +bool MPU6050_Base::getClockOutputEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_CLKOUT_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set reference clock output enabled status. + * When this bit is equal to 1, a reference clock output is provided at the + * CLKOUT pin. When this bit is equal to 0, the clock output is disabled. For + * further information regarding CLKOUT, please refer to the MPU-60X0 Product + * Specification document. + * @param enabled New reference clock output enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_CLKOUT_EN_BIT + */ +void MPU6050_Base::setClockOutputEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_CLKOUT_EN_BIT, enabled, wireObj); +} + +// INT_ENABLE register + +/** Get full interrupt enabled status. + * Full register byte for all interrupts, for quick reading. Each bit will be + * set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +uint8_t MPU6050_Base::getIntEnabled() { + I2Cdev::readByte(devAddr, MPU6050_RA_INT_ENABLE, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set full interrupt enabled status. + * Full register byte for all interrupts, for quick reading. Each bit should be + * set 0 for disabled, 1 for enabled. + * @param enabled New interrupt enabled status + * @see getIntFreefallEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +void MPU6050_Base::setIntEnabled(uint8_t enabled) { + I2Cdev::writeByte(devAddr, MPU6050_RA_INT_ENABLE, enabled, wireObj); +} +/** Get Free Fall interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +bool MPU6050_Base::getIntFreefallEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FF_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Free Fall interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntFreefallEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +void MPU6050_Base::setIntFreefallEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FF_BIT, enabled, wireObj); +} +/** Get Motion Detection interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_MOT_BIT + **/ +bool MPU6050_Base::getIntMotionEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_MOT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Motion Detection interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntMotionEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_MOT_BIT + **/ +void MPU6050_Base::setIntMotionEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_MOT_BIT, enabled, wireObj); +} +/** Get Zero Motion Detection interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_ZMOT_BIT + **/ +bool MPU6050_Base::getIntZeroMotionEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_ZMOT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Zero Motion Detection interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntZeroMotionEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_ZMOT_BIT + **/ +void MPU6050_Base::setIntZeroMotionEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_ZMOT_BIT, enabled, wireObj); +} +/** Get FIFO Buffer Overflow interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FIFO_OFLOW_BIT + **/ +bool MPU6050_Base::getIntFIFOBufferOverflowEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FIFO_OFLOW_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set FIFO Buffer Overflow interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntFIFOBufferOverflowEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FIFO_OFLOW_BIT + **/ +void MPU6050_Base::setIntFIFOBufferOverflowEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FIFO_OFLOW_BIT, enabled, wireObj); +} +/** Get I2C Master interrupt enabled status. + * This enables any of the I2C Master interrupt sources to generate an + * interrupt. Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_I2C_MST_INT_BIT + **/ +bool MPU6050_Base::getIntI2CMasterEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_I2C_MST_INT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set I2C Master interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntI2CMasterEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_I2C_MST_INT_BIT + **/ +void MPU6050_Base::setIntI2CMasterEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_I2C_MST_INT_BIT, enabled, wireObj); +} +/** Get Data Ready interrupt enabled setting. + * This event occurs each time a write operation to all of the sensor registers + * has been completed. Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_DATA_RDY_BIT + */ +bool MPU6050_Base::getIntDataReadyEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DATA_RDY_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Data Ready interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntDataReadyEnabled() + * @see MPU6050_RA_INT_CFG + * @see MPU6050_INTERRUPT_DATA_RDY_BIT + */ +void MPU6050_Base::setIntDataReadyEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DATA_RDY_BIT, enabled, wireObj); +} + +// INT_STATUS register + +/** Get full set of interrupt status bits. + * These bits clear to 0 after the register has been read. Very useful + * for getting multiple INT statuses, since each single bit read clears + * all of them because it has to read the whole byte. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + */ +uint8_t MPU6050_Base::getIntStatus() { + I2Cdev::readByte(devAddr, MPU6050_RA_INT_STATUS, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Free Fall interrupt status. + * This bit automatically sets to 1 when a Free Fall interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_FF_BIT + */ +bool MPU6050_Base::getIntFreefallStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_FF_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Motion Detection interrupt status. + * This bit automatically sets to 1 when a Motion Detection interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_MOT_BIT + */ +bool MPU6050_Base::getIntMotionStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_MOT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Zero Motion Detection interrupt status. + * This bit automatically sets to 1 when a Zero Motion Detection interrupt has + * been generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_ZMOT_BIT + */ +bool MPU6050_Base::getIntZeroMotionStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_ZMOT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get FIFO Buffer Overflow interrupt status. + * This bit automatically sets to 1 when a Free Fall interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_FIFO_OFLOW_BIT + */ +bool MPU6050_Base::getIntFIFOBufferOverflowStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_FIFO_OFLOW_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get I2C Master interrupt status. + * This bit automatically sets to 1 when an I2C Master interrupt has been + * generated. For a list of I2C Master interrupts, please refer to Register 54. + * The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_I2C_MST_INT_BIT + */ +bool MPU6050_Base::getIntI2CMasterStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_I2C_MST_INT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Data Ready interrupt status. + * This bit automatically sets to 1 when a Data Ready interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_DATA_RDY_BIT + */ +bool MPU6050_Base::getIntDataReadyStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_DATA_RDY_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} + +// ACCEL_*OUT_* registers + +/** Get raw 9-axis motion sensor readings (accel/gyro/compass). + * FUNCTION NOT FULLY IMPLEMENTED YET. + * @param ax 16-bit signed integer container for accelerometer X-axis value + * @param ay 16-bit signed integer container for accelerometer Y-axis value + * @param az 16-bit signed integer container for accelerometer Z-axis value + * @param gx 16-bit signed integer container for gyroscope X-axis value + * @param gy 16-bit signed integer container for gyroscope Y-axis value + * @param gz 16-bit signed integer container for gyroscope Z-axis value + * @param mx 16-bit signed integer container for magnetometer X-axis value + * @param my 16-bit signed integer container for magnetometer Y-axis value + * @param mz 16-bit signed integer container for magnetometer Z-axis value + * @see getMotion6() + * @see getAcceleration() + * @see getRotation() + * @see MPU6050_RA_ACCEL_XOUT_H + */ +void MPU6050_Base::getMotion9(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz, int16_t* mx, int16_t* my, int16_t* mz) { + (void)mx; // unused parameter + (void)my; // unused parameter + (void)mz; // unused parameter + + getMotion6(ax, ay, az, gx, gy, gz); + // TODO: magnetometer integration +} +/** Get raw 6-axis motion sensor readings (accel/gyro). + * Retrieves all currently available motion sensor values. + * @param ax 16-bit signed integer container for accelerometer X-axis value + * @param ay 16-bit signed integer container for accelerometer Y-axis value + * @param az 16-bit signed integer container for accelerometer Z-axis value + * @param gx 16-bit signed integer container for gyroscope X-axis value + * @param gy 16-bit signed integer container for gyroscope Y-axis value + * @param gz 16-bit signed integer container for gyroscope Z-axis value + * @see getAcceleration() + * @see getRotation() + * @see MPU6050_RA_ACCEL_XOUT_H + */ +void MPU6050_Base::getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz) { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_XOUT_H, 14, buffer, I2Cdev::readTimeout, wireObj); + *ax = (((int16_t)buffer[0]) << 8) | buffer[1]; + *ay = (((int16_t)buffer[2]) << 8) | buffer[3]; + *az = (((int16_t)buffer[4]) << 8) | buffer[5]; + *gx = (((int16_t)buffer[8]) << 8) | buffer[9]; + *gy = (((int16_t)buffer[10]) << 8) | buffer[11]; + *gz = (((int16_t)buffer[12]) << 8) | buffer[13]; +} +/** Get 3-axis accelerometer readings. + * These registers store the most recent accelerometer measurements. + * Accelerometer measurements are written to these registers at the Sample Rate + * as defined in Register 25. + * + * The accelerometer measurement registers, along with the temperature + * measurement registers, gyroscope measurement registers, and external sensor + * data registers, are composed of two sets of registers: an internal register + * set and a user-facing read register set. + * + * The data within the accelerometer sensors' internal register set is always + * updated at the Sample Rate. Meanwhile, the user-facing read register set + * duplicates the internal register set's data values whenever the serial + * interface is idle. This guarantees that a burst read of sensor registers will + * read measurements from the same sampling instant. Note that if burst reads + * are not used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * Each 16-bit accelerometer measurement has a full scale defined in ACCEL_FS + * (Register 28). For each full scale setting, the accelerometers' sensitivity + * per LSB in ACCEL_xOUT is shown in the table below: + * + *
+ * AFS_SEL | Full Scale Range | LSB Sensitivity
+ * --------+------------------+----------------
+ * 0       | +/- 2g           | 16384 LSB/mg
+ * 1       | +/- 4g           | 8192 LSB/mg
+ * 2       | +/- 8g           | 4096 LSB/mg
+ * 3       | +/- 16g          | 2048 LSB/mg
+ * 
+ * + * @param x 16-bit signed integer container for X-axis acceleration + * @param y 16-bit signed integer container for Y-axis acceleration + * @param z 16-bit signed integer container for Z-axis acceleration + * @see MPU6050_RA_GYRO_XOUT_H + */ +void MPU6050_Base::getAcceleration(int16_t* x, int16_t* y, int16_t* z) { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_XOUT_H, 6, buffer, I2Cdev::readTimeout, wireObj); + *x = (((int16_t)buffer[0]) << 8) | buffer[1]; + *y = (((int16_t)buffer[2]) << 8) | buffer[3]; + *z = (((int16_t)buffer[4]) << 8) | buffer[5]; +} +/** Get X-axis accelerometer reading. + * @return X-axis acceleration measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_ACCEL_XOUT_H + */ +int16_t MPU6050_Base::getAccelerationX() { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_XOUT_H, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Y-axis accelerometer reading. + * @return Y-axis acceleration measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_ACCEL_YOUT_H + */ +int16_t MPU6050_Base::getAccelerationY() { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_YOUT_H, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Z-axis accelerometer reading. + * @return Z-axis acceleration measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_ACCEL_ZOUT_H + */ +int16_t MPU6050_Base::getAccelerationZ() { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_ZOUT_H, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} + +// TEMP_OUT_* registers + +/** Get current internal temperature. + * @return Temperature reading in 16-bit 2's complement format + * @see MPU6050_RA_TEMP_OUT_H + */ +int16_t MPU6050_Base::getTemperature() { + I2Cdev::readBytes(devAddr, MPU6050_RA_TEMP_OUT_H, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} + +// GYRO_*OUT_* registers + +/** Get 3-axis gyroscope readings. + * These gyroscope measurement registers, along with the accelerometer + * measurement registers, temperature measurement registers, and external sensor + * data registers, are composed of two sets of registers: an internal register + * set and a user-facing read register set. + * The data within the gyroscope sensors' internal register set is always + * updated at the Sample Rate. Meanwhile, the user-facing read register set + * duplicates the internal register set's data values whenever the serial + * interface is idle. This guarantees that a burst read of sensor registers will + * read measurements from the same sampling instant. Note that if burst reads + * are not used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * Each 16-bit gyroscope measurement has a full scale defined in FS_SEL + * (Register 27). For each full scale setting, the gyroscopes' sensitivity per + * LSB in GYRO_xOUT is shown in the table below: + * + *
+ * FS_SEL | Full Scale Range   | LSB Sensitivity
+ * -------+--------------------+----------------
+ * 0      | +/- 250 degrees/s  | 131 LSB/deg/s
+ * 1      | +/- 500 degrees/s  | 65.5 LSB/deg/s
+ * 2      | +/- 1000 degrees/s | 32.8 LSB/deg/s
+ * 3      | +/- 2000 degrees/s | 16.4 LSB/deg/s
+ * 
+ * + * @param x 16-bit signed integer container for X-axis rotation + * @param y 16-bit signed integer container for Y-axis rotation + * @param z 16-bit signed integer container for Z-axis rotation + * @see getMotion6() + * @see MPU6050_RA_GYRO_XOUT_H + */ +void MPU6050_Base::getRotation(int16_t* x, int16_t* y, int16_t* z) { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_XOUT_H, 6, buffer, I2Cdev::readTimeout, wireObj); + *x = (((int16_t)buffer[0]) << 8) | buffer[1]; + *y = (((int16_t)buffer[2]) << 8) | buffer[3]; + *z = (((int16_t)buffer[4]) << 8) | buffer[5]; +} +/** Get X-axis gyroscope reading. + * @return X-axis rotation measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_GYRO_XOUT_H + */ +int16_t MPU6050_Base::getRotationX() { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_XOUT_H, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Y-axis gyroscope reading. + * @return Y-axis rotation measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_GYRO_YOUT_H + */ +int16_t MPU6050_Base::getRotationY() { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_YOUT_H, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Z-axis gyroscope reading. + * @return Z-axis rotation measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_GYRO_ZOUT_H + */ +int16_t MPU6050_Base::getRotationZ() { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_ZOUT_H, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} + +// EXT_SENS_DATA_* registers + +/** Read single byte from external sensor data register. + * These registers store data read from external sensors by the Slave 0, 1, 2, + * and 3 on the auxiliary I2C interface. Data read by Slave 4 is stored in + * I2C_SLV4_DI (Register 53). + * + * External sensor data is written to these registers at the Sample Rate as + * defined in Register 25. This access rate can be reduced by using the Slave + * Delay Enable registers (Register 103). + * + * External sensor data registers, along with the gyroscope measurement + * registers, accelerometer measurement registers, and temperature measurement + * registers, are composed of two sets of registers: an internal register set + * and a user-facing read register set. + * + * The data within the external sensors' internal register set is always updated + * at the Sample Rate (or the reduced access rate) whenever the serial interface + * is idle. This guarantees that a burst read of sensor registers will read + * measurements from the same sampling instant. Note that if burst reads are not + * used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * Data is placed in these external sensor data registers according to + * I2C_SLV0_CTRL, I2C_SLV1_CTRL, I2C_SLV2_CTRL, and I2C_SLV3_CTRL (Registers 39, + * 42, 45, and 48). When more than zero bytes are read (I2C_SLVx_LEN > 0) from + * an enabled slave (I2C_SLVx_EN = 1), the slave is read at the Sample Rate (as + * defined in Register 25) or delayed rate (if specified in Register 52 and + * 103). During each Sample cycle, slave reads are performed in order of Slave + * number. If all slaves are enabled with more than zero bytes to be read, the + * order will be Slave 0, followed by Slave 1, Slave 2, and Slave 3. + * + * Each enabled slave will have EXT_SENS_DATA registers associated with it by + * number of bytes read (I2C_SLVx_LEN) in order of slave number, starting from + * EXT_SENS_DATA_00. Note that this means enabling or disabling a slave may + * change the higher numbered slaves' associated registers. Furthermore, if + * fewer total bytes are being read from the external sensors as a result of + * such a change, then the data remaining in the registers which no longer have + * an associated slave device (i.e. high numbered registers) will remain in + * these previously allocated registers unless reset. + * + * If the sum of the read lengths of all SLVx transactions exceed the number of + * available EXT_SENS_DATA registers, the excess bytes will be dropped. There + * are 24 EXT_SENS_DATA registers and hence the total read lengths between all + * the slaves cannot be greater than 24 or some bytes will be lost. + * + * Note: Slave 4's behavior is distinct from that of Slaves 0-3. For further + * information regarding the characteristics of Slave 4, please refer to + * Registers 49 to 53. + * + * EXAMPLE: + * Suppose that Slave 0 is enabled with 4 bytes to be read (I2C_SLV0_EN = 1 and + * I2C_SLV0_LEN = 4) while Slave 1 is enabled with 2 bytes to be read so that + * I2C_SLV1_EN = 1 and I2C_SLV1_LEN = 2. In such a situation, EXT_SENS_DATA _00 + * through _03 will be associated with Slave 0, while EXT_SENS_DATA _04 and 05 + * will be associated with Slave 1. If Slave 2 is enabled as well, registers + * starting from EXT_SENS_DATA_06 will be allocated to Slave 2. + * + * If Slave 2 is disabled while Slave 3 is enabled in this same situation, then + * registers starting from EXT_SENS_DATA_06 will be allocated to Slave 3 + * instead. + * + * REGISTER ALLOCATION FOR DYNAMIC DISABLE VS. NORMAL DISABLE: + * If a slave is disabled at any time, the space initially allocated to the + * slave in the EXT_SENS_DATA register, will remain associated with that slave. + * This is to avoid dynamic adjustment of the register allocation. + * + * The allocation of the EXT_SENS_DATA registers is recomputed only when (1) all + * slaves are disabled, or (2) the I2C_MST_RST bit is set (Register 106). + * + * This above is also true if one of the slaves gets NACKed and stops + * functioning. + * + * @param position Starting position (0-23) + * @return Byte read from register + */ +uint8_t MPU6050_Base::getExternalSensorByte(int position) { + I2Cdev::readByte(devAddr, MPU6050_RA_EXT_SENS_DATA_00 + position, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Read word (2 bytes) from external sensor data registers. + * @param position Starting position (0-21) + * @return Word read from register + * @see getExternalSensorByte() + */ +uint16_t MPU6050_Base::getExternalSensorWord(int position) { + I2Cdev::readBytes(devAddr, MPU6050_RA_EXT_SENS_DATA_00 + position, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((uint16_t)buffer[0]) << 8) | buffer[1]; +} +/** Read double word (4 bytes) from external sensor data registers. + * @param position Starting position (0-20) + * @return Double word read from registers + * @see getExternalSensorByte() + */ +uint32_t MPU6050_Base::getExternalSensorDWord(int position) { + I2Cdev::readBytes(devAddr, MPU6050_RA_EXT_SENS_DATA_00 + position, 4, buffer, I2Cdev::readTimeout, wireObj); + return (((uint32_t)buffer[0]) << 24) | (((uint32_t)buffer[1]) << 16) | (((uint16_t)buffer[2]) << 8) | buffer[3]; +} + +// MOT_DETECT_STATUS register + +/** Get full motion detection status register content (all bits). + * @return Motion detection status byte + * @see MPU6050_RA_MOT_DETECT_STATUS + */ +uint8_t MPU6050_Base::getMotionStatus() { + I2Cdev::readByte(devAddr, MPU6050_RA_MOT_DETECT_STATUS, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get X-axis negative motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_XNEG_BIT + */ +bool MPU6050_Base::getXNegMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_XNEG_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get X-axis positive motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_XPOS_BIT + */ +bool MPU6050_Base::getXPosMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_XPOS_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Y-axis negative motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_YNEG_BIT + */ +bool MPU6050_Base::getYNegMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_YNEG_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Y-axis positive motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_YPOS_BIT + */ +bool MPU6050_Base::getYPosMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_YPOS_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Z-axis negative motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_ZNEG_BIT + */ +bool MPU6050_Base::getZNegMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_ZNEG_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get Z-axis positive motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_ZPOS_BIT + */ +bool MPU6050_Base::getZPosMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_ZPOS_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Get zero motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_ZRMOT_BIT + */ +bool MPU6050_Base::getZeroMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_ZRMOT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} + +// I2C_SLV*_DO register + +/** Write byte to Data Output container for specified slave. + * This register holds the output data written into Slave when Slave is set to + * write mode. For further information regarding Slave control, please + * refer to Registers 37 to 39 and immediately following. + * @param num Slave number (0-3) + * @param data Byte to write + * @see MPU6050_RA_I2C_SLV0_DO + */ +void MPU6050_Base::setSlaveOutputByte(uint8_t num, uint8_t data) { + if (num > 3) return; + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV0_DO + num, data, wireObj); +} + +// I2C_MST_DELAY_CTRL register + +/** Get external data shadow delay enabled status. + * This register is used to specify the timing of external sensor data + * shadowing. When DELAY_ES_SHADOW is set to 1, shadowing of external + * sensor data is delayed until all data has been received. + * @return Current external data shadow delay enabled status. + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT + */ +bool MPU6050_Base::getExternalShadowDelayEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set external data shadow delay enabled status. + * @param enabled New external data shadow delay enabled status. + * @see getExternalShadowDelayEnabled() + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT + */ +void MPU6050_Base::setExternalShadowDelayEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT, enabled, wireObj); +} +/** Get slave delay enabled status. + * When a particular slave delay is enabled, the rate of access for the that + * slave device is reduced. When a slave's access rate is decreased relative to + * the Sample Rate, the slave is accessed every: + * + * 1 / (1 + I2C_MST_DLY) Samples + * + * This base Sample Rate in turn is determined by SMPLRT_DIV (register * 25) + * and DLPF_CFG (register 26). + * + * For further information regarding I2C_MST_DLY, please refer to register 52. + * For further information regarding the Sample Rate, please refer to register 25. + * + * @param num Slave number (0-4) + * @return Current slave delay enabled status. + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT + */ +bool MPU6050_Base::getSlaveDelayEnabled(uint8_t num) { + // MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT is 4, SLV3 is 3, etc. + if (num > 4) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, num, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set slave delay enabled status. + * @param num Slave number (0-4) + * @param enabled New slave delay enabled status. + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT + */ +void MPU6050_Base::setSlaveDelayEnabled(uint8_t num, bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, num, enabled, wireObj); +} + +// SIGNAL_PATH_RESET register + +/** Reset gyroscope signal path. + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * @see MPU6050_RA_SIGNAL_PATH_RESET + * @see MPU6050_PATHRESET_GYRO_RESET_BIT + */ +void MPU6050_Base::resetGyroscopePath() { + I2Cdev::writeBit(devAddr, MPU6050_RA_SIGNAL_PATH_RESET, MPU6050_PATHRESET_GYRO_RESET_BIT, true, wireObj); +} +/** Reset accelerometer signal path. + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * @see MPU6050_RA_SIGNAL_PATH_RESET + * @see MPU6050_PATHRESET_ACCEL_RESET_BIT + */ +void MPU6050_Base::resetAccelerometerPath() { + I2Cdev::writeBit(devAddr, MPU6050_RA_SIGNAL_PATH_RESET, MPU6050_PATHRESET_ACCEL_RESET_BIT, true, wireObj); +} +/** Reset temperature sensor signal path. + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * @see MPU6050_RA_SIGNAL_PATH_RESET + * @see MPU6050_PATHRESET_TEMP_RESET_BIT + */ +void MPU6050_Base::resetTemperaturePath() { + I2Cdev::writeBit(devAddr, MPU6050_RA_SIGNAL_PATH_RESET, MPU6050_PATHRESET_TEMP_RESET_BIT, true, wireObj); +} + +// MOT_DETECT_CTRL register + +/** Get accelerometer power-on delay. + * The accelerometer data path provides samples to the sensor registers, Motion + * detection, Zero Motion detection, and Free Fall detection modules. The + * signal path contains filters which must be flushed on wake-up with new + * samples before the detection modules begin operations. The default wake-up + * delay, of 4ms can be lengthened by up to 3ms. This additional delay is + * specified in ACCEL_ON_DELAY in units of 1 LSB = 1 ms. The user may select + * any value above zero unless instructed otherwise by InvenSense. Please refer + * to Section 8 of the MPU-6000/MPU-6050 Product Specification document for + * further information regarding the detection modules. + * @return Current accelerometer power-on delay + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_ACCEL_ON_DELAY_BIT + */ +uint8_t MPU6050_Base::getAccelerometerPowerOnDelay() { + I2Cdev::readBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_ACCEL_ON_DELAY_BIT, MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set accelerometer power-on delay. + * @param delay New accelerometer power-on delay (0-3) + * @see getAccelerometerPowerOnDelay() + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_ACCEL_ON_DELAY_BIT + */ +void MPU6050_Base::setAccelerometerPowerOnDelay(uint8_t delay) { + I2Cdev::writeBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_ACCEL_ON_DELAY_BIT, MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH, delay, wireObj); +} +/** Get Free Fall detection counter decrement configuration. + * Detection is registered by the Free Fall detection module after accelerometer + * measurements meet their respective threshold conditions over a specified + * number of samples. When the threshold conditions are met, the corresponding + * detection counter increments by 1. The user may control the rate at which the + * detection counter decrements when the threshold condition is not met by + * configuring FF_COUNT. The decrement rate can be set according to the + * following table: + * + *
+ * FF_COUNT | Counter Decrement
+ * ---------+------------------
+ * 0        | Reset
+ * 1        | 1
+ * 2        | 2
+ * 3        | 4
+ * 
+ * + * When FF_COUNT is configured to 0 (reset), any non-qualifying sample will + * reset the counter to 0. For further information on Free Fall detection, + * please refer to Registers 29 to 32. + * + * @return Current decrement configuration + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_FF_COUNT_BIT + */ +uint8_t MPU6050_Base::getFreefallDetectionCounterDecrement() { + I2Cdev::readBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_FF_COUNT_BIT, MPU6050_DETECT_FF_COUNT_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Free Fall detection counter decrement configuration. + * @param decrement New decrement configuration value + * @see getFreefallDetectionCounterDecrement() + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_FF_COUNT_BIT + */ +void MPU6050_Base::setFreefallDetectionCounterDecrement(uint8_t decrement) { + I2Cdev::writeBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_FF_COUNT_BIT, MPU6050_DETECT_FF_COUNT_LENGTH, decrement, wireObj); +} +/** Get Motion detection counter decrement configuration. + * Detection is registered by the Motion detection module after accelerometer + * measurements meet their respective threshold conditions over a specified + * number of samples. When the threshold conditions are met, the corresponding + * detection counter increments by 1. The user may control the rate at which the + * detection counter decrements when the threshold condition is not met by + * configuring MOT_COUNT. The decrement rate can be set according to the + * following table: + * + *
+ * MOT_COUNT | Counter Decrement
+ * ----------+------------------
+ * 0         | Reset
+ * 1         | 1
+ * 2         | 2
+ * 3         | 4
+ * 
+ * + * When MOT_COUNT is configured to 0 (reset), any non-qualifying sample will + * reset the counter to 0. For further information on Motion detection, + * please refer to Registers 29 to 32. + * + */ +uint8_t MPU6050_Base::getMotionDetectionCounterDecrement() { + I2Cdev::readBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_MOT_COUNT_BIT, MPU6050_DETECT_MOT_COUNT_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Motion detection counter decrement configuration. + * @param decrement New decrement configuration value + * @see getMotionDetectionCounterDecrement() + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_MOT_COUNT_BIT + */ +void MPU6050_Base::setMotionDetectionCounterDecrement(uint8_t decrement) { + I2Cdev::writeBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_MOT_COUNT_BIT, MPU6050_DETECT_MOT_COUNT_LENGTH, decrement, wireObj); +} + +// USER_CTRL register + +/** Get FIFO enabled status. + * When this bit is set to 0, the FIFO buffer is disabled. The FIFO buffer + * cannot be written to or read from while disabled. The FIFO buffer's state + * does not change unless the MPU-60X0 is power cycled. + * @return Current FIFO enabled status + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_FIFO_EN_BIT + */ +bool MPU6050_Base::getFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_FIFO_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set FIFO enabled status. + * @param enabled New FIFO enabled status + * @see getFIFOEnabled() + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_FIFO_EN_BIT + */ +void MPU6050_Base::setFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_FIFO_EN_BIT, enabled, wireObj); +} +/** Get I2C Master Mode enabled status. + * When this mode is enabled, the MPU-60X0 acts as the I2C Master to the + * external sensor slave devices on the auxiliary I2C bus. When this bit is + * cleared to 0, the auxiliary I2C bus lines (AUX_DA and AUX_CL) are logically + * driven by the primary I2C bus (SDA and SCL). This is a precondition to + * enabling Bypass Mode. For further information regarding Bypass Mode, please + * refer to Register 55. + * @return Current I2C Master Mode enabled status + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_I2C_MST_EN_BIT + */ +bool MPU6050_Base::getI2CMasterModeEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_MST_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set I2C Master Mode enabled status. + * @param enabled New I2C Master Mode enabled status + * @see getI2CMasterModeEnabled() + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_I2C_MST_EN_BIT + */ +void MPU6050_Base::setI2CMasterModeEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_MST_EN_BIT, enabled, wireObj); +} +/** Switch from I2C to SPI mode (MPU-6000 only) + * If this is set, the primary SPI interface will be enabled in place of the + * disabled primary I2C interface. + */ +void MPU6050_Base::switchSPIEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_IF_DIS_BIT, enabled, wireObj); +} +/** Reset the FIFO. + * This bit resets the FIFO buffer when set to 1 while FIFO_EN equals 0. This + * bit automatically clears to 0 after the reset has been triggered. + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_FIFO_RESET_BIT + */ +void MPU6050_Base::resetFIFO() { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_FIFO_RESET_BIT, true, wireObj); +} +/** Reset the I2C Master. + * This bit resets the I2C Master when set to 1 while I2C_MST_EN equals 0. + * This bit automatically clears to 0 after the reset has been triggered. + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_I2C_MST_RESET_BIT + */ +void MPU6050_Base::resetI2CMaster() { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_MST_RESET_BIT, true, wireObj); +} +/** Reset all sensor registers and signal paths. + * When set to 1, this bit resets the signal paths for all sensors (gyroscopes, + * accelerometers, and temperature sensor). This operation will also clear the + * sensor registers. This bit automatically clears to 0 after the reset has been + * triggered. + * + * When resetting only the signal path (and not the sensor registers), please + * use Register 104, SIGNAL_PATH_RESET. + * + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_SIG_COND_RESET_BIT + */ +void MPU6050_Base::resetSensors() { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_SIG_COND_RESET_BIT, true, wireObj); +} + +// PWR_MGMT_1 register + +/** Trigger a full device reset. + * A small delay of ~50ms may be desirable after triggering a reset. + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_DEVICE_RESET_BIT + */ +void MPU6050_Base::reset() { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_DEVICE_RESET_BIT, true, wireObj); +} +/** Get sleep mode status. + * Setting the SLEEP bit in the register puts the device into very low power + * sleep mode. In this mode, only the serial interface and internal registers + * remain active, allowing for a very low standby current. Clearing this bit + * puts the device back into normal mode. To save power, the individual standby + * selections for each of the gyros should be used if any gyro axis is not used + * by the application. + * @return Current sleep mode enabled status + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_SLEEP_BIT + */ +bool MPU6050_Base::getSleepEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set sleep mode status. + * @param enabled New sleep mode enabled status + * @see getSleepEnabled() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_SLEEP_BIT + */ +void MPU6050_Base::setSleepEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, enabled, wireObj); +} +/** Get wake cycle enabled status. + * When this bit is set to 1 and SLEEP is disabled, the MPU-60X0 will cycle + * between sleep mode and waking up to take a single sample of data from active + * sensors at a rate determined by LP_WAKE_CTRL (register 108). + * @return Current sleep mode enabled status + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CYCLE_BIT + */ +bool MPU6050_Base::getWakeCycleEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CYCLE_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set wake cycle enabled status. + * @param enabled New sleep mode enabled status + * @see getWakeCycleEnabled() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CYCLE_BIT + */ +void MPU6050_Base::setWakeCycleEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CYCLE_BIT, enabled, wireObj); +} +/** Get temperature sensor enabled status. + * Control the usage of the internal temperature sensor. + * + * Note: this register stores the *disabled* value, but for consistency with the + * rest of the code, the function is named and used with standard true/false + * values to indicate whether the sensor is enabled or disabled, respectively. + * + * @return Current temperature sensor enabled status + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_TEMP_DIS_BIT + */ +bool MPU6050_Base::getTempSensorEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0] == 0; // 1 is actually disabled here +} +/** Set temperature sensor enabled status. + * Note: this register stores the *disabled* value, but for consistency with the + * rest of the code, the function is named and used with standard true/false + * values to indicate whether the sensor is enabled or disabled, respectively. + * + * @param enabled New temperature sensor enabled status + * @see getTempSensorEnabled() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_TEMP_DIS_BIT + */ +void MPU6050_Base::setTempSensorEnabled(bool enabled) { + // 1 is actually disabled here + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, !enabled, wireObj); +} +/** Get clock source setting. + * @return Current clock source setting + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CLKSEL_BIT + * @see MPU6050_PWR1_CLKSEL_LENGTH + */ +uint8_t MPU6050_Base::getClockSource() { + I2Cdev::readBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set clock source setting. + * An internal 8MHz oscillator, gyroscope based clock, or external sources can + * be selected as the MPU-60X0 clock source. When the internal 8 MHz oscillator + * or an external source is chosen as the clock source, the MPU-60X0 can operate + * in low power modes with the gyroscopes disabled. + * + * Upon power up, the MPU-60X0 clock source defaults to the internal oscillator. + * However, it is highly recommended that the device be configured to use one of + * the gyroscopes (or an external clock source) as the clock reference for + * improved stability. The clock source can be selected according to the following table: + * + *
+ * CLK_SEL | Clock Source
+ * --------+--------------------------------------
+ * 0       | Internal oscillator
+ * 1       | PLL with X Gyro reference
+ * 2       | PLL with Y Gyro reference
+ * 3       | PLL with Z Gyro reference
+ * 4       | PLL with external 32.768kHz reference
+ * 5       | PLL with external 19.2MHz reference
+ * 6       | Reserved
+ * 7       | Stops the clock and keeps the timing generator in reset
+ * 
+ * + * @param source New clock source setting + * @see getClockSource() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CLKSEL_BIT + * @see MPU6050_PWR1_CLKSEL_LENGTH + */ +void MPU6050_Base::setClockSource(uint8_t source) { + I2Cdev::writeBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, source, wireObj); +} + +// PWR_MGMT_2 register + +/** Get wake frequency in Accel-Only Low Power Mode. + * The MPU-60X0 can be put into Accerlerometer Only Low Power Mode by setting + * PWRSEL to 1 in the Power Management 1 register (Register 107). In this mode, + * the device will power off all devices except for the primary I2C interface, + * waking only the accelerometer at fixed intervals to take a single + * measurement. The frequency of wake-ups can be configured with LP_WAKE_CTRL + * as shown below: + * + *
+ * LP_WAKE_CTRL | Wake-up Frequency
+ * -------------+------------------
+ * 0            | 1.25 Hz
+ * 1            | 2.5 Hz
+ * 2            | 20 Hz
+ * 3            | 40 Hz
+ * 
+ * + * For further information regarding the MPU-60X0's power modes, please refer to + * Register 107. + * + * @return Current wake frequency + * @see MPU6050_RA_PWR_MGMT_2 + */ +uint8_t MPU6050_Base::getWakeFrequency() { + I2Cdev::readBits(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_LP_WAKE_CTRL_BIT, MPU6050_PWR2_LP_WAKE_CTRL_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set wake frequency in Accel-Only Low Power Mode. + * @param frequency New wake frequency + * @see MPU6050_RA_PWR_MGMT_2 + */ +void MPU6050_Base::setWakeFrequency(uint8_t frequency) { + I2Cdev::writeBits(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_LP_WAKE_CTRL_BIT, MPU6050_PWR2_LP_WAKE_CTRL_LENGTH, frequency, wireObj); +} + +/** Get X-axis accelerometer standby enabled status. + * If enabled, the X-axis will not gather or report data (or use power). + * @return Current X-axis standby enabled status + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_XA_BIT + */ +bool MPU6050_Base::getStandbyXAccelEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XA_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set X-axis accelerometer standby enabled status. + * @param New X-axis standby enabled status + * @see getStandbyXAccelEnabled() + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_XA_BIT + */ +void MPU6050_Base::setStandbyXAccelEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XA_BIT, enabled, wireObj); +} +/** Get Y-axis accelerometer standby enabled status. + * If enabled, the Y-axis will not gather or report data (or use power). + * @return Current Y-axis standby enabled status + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_YA_BIT + */ +bool MPU6050_Base::getStandbyYAccelEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YA_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Y-axis accelerometer standby enabled status. + * @param New Y-axis standby enabled status + * @see getStandbyYAccelEnabled() + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_YA_BIT + */ +void MPU6050_Base::setStandbyYAccelEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YA_BIT, enabled, wireObj); +} +/** Get Z-axis accelerometer standby enabled status. + * If enabled, the Z-axis will not gather or report data (or use power). + * @return Current Z-axis standby enabled status + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_ZA_BIT + */ +bool MPU6050_Base::getStandbyZAccelEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZA_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Z-axis accelerometer standby enabled status. + * @param New Z-axis standby enabled status + * @see getStandbyZAccelEnabled() + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_ZA_BIT + */ +void MPU6050_Base::setStandbyZAccelEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZA_BIT, enabled, wireObj); +} +/** Get X-axis gyroscope standby enabled status. + * If enabled, the X-axis will not gather or report data (or use power). + * @return Current X-axis standby enabled status + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_XG_BIT + */ +bool MPU6050_Base::getStandbyXGyroEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XG_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set X-axis gyroscope standby enabled status. + * @param New X-axis standby enabled status + * @see getStandbyXGyroEnabled() + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_XG_BIT + */ +void MPU6050_Base::setStandbyXGyroEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XG_BIT, enabled, wireObj); +} +/** Get Y-axis gyroscope standby enabled status. + * If enabled, the Y-axis will not gather or report data (or use power). + * @return Current Y-axis standby enabled status + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_YG_BIT + */ +bool MPU6050_Base::getStandbyYGyroEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YG_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Y-axis gyroscope standby enabled status. + * @param New Y-axis standby enabled status + * @see getStandbyYGyroEnabled() + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_YG_BIT + */ +void MPU6050_Base::setStandbyYGyroEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YG_BIT, enabled, wireObj); +} +/** Get Z-axis gyroscope standby enabled status. + * If enabled, the Z-axis will not gather or report data (or use power). + * @return Current Z-axis standby enabled status + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_ZG_BIT + */ +bool MPU6050_Base::getStandbyZGyroEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZG_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Z-axis gyroscope standby enabled status. + * @param New Z-axis standby enabled status + * @see getStandbyZGyroEnabled() + * @see MPU6050_RA_PWR_MGMT_2 + * @see MPU6050_PWR2_STBY_ZG_BIT + */ +void MPU6050_Base::setStandbyZGyroEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZG_BIT, enabled, wireObj); +} + +// FIFO_COUNT* registers + +/** Get current FIFO buffer size. + * This value indicates the number of bytes stored in the FIFO buffer. This + * number is in turn the number of bytes that can be read from the FIFO buffer + * and it is directly proportional to the number of samples available given the + * set of sensor data bound to be stored in the FIFO (register 35 and 36). + * @return Current FIFO buffer size + */ +uint16_t MPU6050_Base::getFIFOCount() { + I2Cdev::readBytes(devAddr, MPU6050_RA_FIFO_COUNTH, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((uint16_t)buffer[0]) << 8) | buffer[1]; +} + +// FIFO_R_W register + +/** Get byte from FIFO buffer. + * This register is used to read and write data from the FIFO buffer. Data is + * written to the FIFO in order of register number (from lowest to highest). If + * all the FIFO enable flags (see below) are enabled and all External Sensor + * Data registers (Registers 73 to 96) are associated with a Slave device, the + * contents of registers 59 through 96 will be written in order at the Sample + * Rate. + * + * The contents of the sensor data registers (Registers 59 to 96) are written + * into the FIFO buffer when their corresponding FIFO enable flags are set to 1 + * in FIFO_EN (Register 35). An additional flag for the sensor data registers + * associated with I2C Slave 3 can be found in I2C_MST_CTRL (Register 36). + * + * If the FIFO buffer has overflowed, the status bit FIFO_OFLOW_INT is + * automatically set to 1. This bit is located in INT_STATUS (Register 58). + * When the FIFO buffer has overflowed, the oldest data will be lost and new + * data will be written to the FIFO. + * + * If the FIFO buffer is empty, reading this register will return the last byte + * that was previously read from the FIFO until new data is available. The user + * should check FIFO_COUNT to ensure that the FIFO buffer is not read when + * empty. + * + * @return Byte from FIFO buffer + */ +uint8_t MPU6050_Base::getFIFOByte() { + I2Cdev::readByte(devAddr, MPU6050_RA_FIFO_R_W, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::getFIFOBytes(uint8_t *data, uint8_t length) { + if(length > 0){ + I2Cdev::readBytes(devAddr, MPU6050_RA_FIFO_R_W, length, data, I2Cdev::readTimeout, wireObj); + } else { + *data = 0; + } +} + +/** Get timeout to get a packet from FIFO buffer. + * @return Current timeout to get a packet from FIFO buffer + * @see MPU6050_FIFO_DEFAULT_TIMEOUT + */ +uint32_t MPU6050_Base::getFIFOTimeout() { + return fifoTimeout; +} + +/** Set timeout to get a packet from FIFO buffer. + * @param New timeout to get a packet from FIFO buffer + * @see MPU6050_FIFO_DEFAULT_TIMEOUT + */ +void MPU6050_Base::setFIFOTimeout(uint32_t fifoTimeout) { + this->fifoTimeout = fifoTimeout; +} + +/** Get latest byte from FIFO buffer no matter how much time has passed. + * === GetCurrentFIFOPacket === + * ================================================================ + * Returns 1) when nothing special was done + * 2) when recovering from overflow + * 0) when no valid data is available + * ================================================================ */ + int8_t MPU6050_Base::GetCurrentFIFOPacket(uint8_t *data, uint8_t length) { // overflow proof + int16_t fifoC; + // This section of code is for when we allowed more than 1 packet to be acquired + uint32_t BreakTimer = micros(); + bool packetReceived = false; + do { + if ((fifoC = getFIFOCount()) > length) { + + if (fifoC > 200) { // if you waited to get the FIFO buffer to > 200 bytes it will take longer to get the last packet in the FIFO Buffer than it will take to reset the buffer and wait for the next to arrive + resetFIFO(); // Fixes any overflow corruption + fifoC = 0; + while (!(fifoC = getFIFOCount()) && ((micros() - BreakTimer) <= (getFIFOTimeout()))); // Get Next New Packet + } else { //We have more than 1 packet but less than 200 bytes of data in the FIFO Buffer + uint8_t Trash[I2CDEVLIB_WIRE_BUFFER_LENGTH]; + while ((fifoC = getFIFOCount()) > length) { // Test each time just in case the MPU is writing to the FIFO Buffer + fifoC = fifoC - length; // Save the last packet + uint16_t RemoveBytes; + while (fifoC) { // fifo count will reach zero so this is safe + RemoveBytes = (fifoC < I2CDEVLIB_WIRE_BUFFER_LENGTH) ? fifoC : I2CDEVLIB_WIRE_BUFFER_LENGTH; // Buffer Length is different than the packet length this will efficiently clear the buffer + getFIFOBytes(Trash, (uint8_t)RemoveBytes); + fifoC -= RemoveBytes; + } + } + } + } + if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset + // We have 1 packet + packetReceived = fifoC == length; + if (!packetReceived && (micros() - BreakTimer) > (getFIFOTimeout())) return 0; + } while (!packetReceived); + getFIFOBytes(data, length); //Get 1 packet + return 1; +} + + +/** Write byte to FIFO buffer. + * @see getFIFOByte() + * @see MPU6050_RA_FIFO_R_W + */ +void MPU6050_Base::setFIFOByte(uint8_t data) { + I2Cdev::writeByte(devAddr, MPU6050_RA_FIFO_R_W, data, wireObj); +} + +// WHO_AM_I register + +/** Get Device ID. + * This register is used to verify the identity of the device (0b110100, 0x34). + * @return Device ID (6 bits only! should be 0x34) + * @see MPU6050_RA_WHO_AM_I + * @see MPU6050_WHO_AM_I_BIT + * @see MPU6050_WHO_AM_I_LENGTH + */ +uint8_t MPU6050_Base::getDeviceID() { + I2Cdev::readBits(devAddr, MPU6050_RA_WHO_AM_I, MPU6050_WHO_AM_I_BIT, MPU6050_WHO_AM_I_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +/** Set Device ID. + * Write a new ID into the WHO_AM_I register (no idea why this should ever be + * necessary though). + * @param id New device ID to set. + * @see getDeviceID() + * @see MPU6050_RA_WHO_AM_I + * @see MPU6050_WHO_AM_I_BIT + * @see MPU6050_WHO_AM_I_LENGTH + */ +void MPU6050_Base::setDeviceID(uint8_t id) { + I2Cdev::writeBits(devAddr, MPU6050_RA_WHO_AM_I, MPU6050_WHO_AM_I_BIT, MPU6050_WHO_AM_I_LENGTH, id, wireObj); +} + +// ======== UNDOCUMENTED/DMP REGISTERS/METHODS ======== + +// XG_OFFS_TC register + +uint8_t MPU6050_Base::getOTPBankValid() { + I2Cdev::readBit(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OTP_BNK_VLD_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setOTPBankValid(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OTP_BNK_VLD_BIT, enabled, wireObj); +} +int8_t MPU6050_Base::getXGyroOffsetTC() { + I2Cdev::readBits(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setXGyroOffsetTC(int8_t offset) { + I2Cdev::writeBits(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset, wireObj); +} + +// YG_OFFS_TC register + +int8_t MPU6050_Base::getYGyroOffsetTC() { + I2Cdev::readBits(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setYGyroOffsetTC(int8_t offset) { + I2Cdev::writeBits(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset, wireObj); +} + +// ZG_OFFS_TC register + +int8_t MPU6050_Base::getZGyroOffsetTC() { + I2Cdev::readBits(devAddr, MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setZGyroOffsetTC(int8_t offset) { + I2Cdev::writeBits(devAddr, MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset, wireObj); +} + +// X_FINE_GAIN register + +int8_t MPU6050_Base::getXFineGain() { + I2Cdev::readByte(devAddr, MPU6050_RA_X_FINE_GAIN, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setXFineGain(int8_t gain) { + I2Cdev::writeByte(devAddr, MPU6050_RA_X_FINE_GAIN, gain, wireObj); +} + +// Y_FINE_GAIN register + +int8_t MPU6050_Base::getYFineGain() { + I2Cdev::readByte(devAddr, MPU6050_RA_Y_FINE_GAIN, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setYFineGain(int8_t gain) { + I2Cdev::writeByte(devAddr, MPU6050_RA_Y_FINE_GAIN, gain, wireObj); +} + +// Z_FINE_GAIN register + +int8_t MPU6050_Base::getZFineGain() { + I2Cdev::readByte(devAddr, MPU6050_RA_Z_FINE_GAIN, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setZFineGain(int8_t gain) { + I2Cdev::writeByte(devAddr, MPU6050_RA_Z_FINE_GAIN, gain, wireObj); +} + +// XA_OFFS_* registers + +int16_t MPU6050_Base::getXAccelOffset() { + uint8_t SaveAddress = ((getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77); // MPU6050,MPU9150 Vs MPU6500,MPU9250 + I2Cdev::readBytes(devAddr, SaveAddress, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +void MPU6050_Base::setXAccelOffset(int16_t offset) { + uint8_t SaveAddress = ((getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77); // MPU6050,MPU9150 Vs MPU6500,MPU9250 + I2Cdev::writeWord(devAddr, SaveAddress, offset, wireObj); +} + +// YA_OFFS_* register + +int16_t MPU6050_Base::getYAccelOffset() { + uint8_t SaveAddress = ((getDeviceID() < 0x38 )? MPU6050_RA_YA_OFFS_H:0x7A); // MPU6050,MPU9150 Vs MPU6500,MPU9250 + I2Cdev::readBytes(devAddr, SaveAddress, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +void MPU6050_Base::setYAccelOffset(int16_t offset) { + uint8_t SaveAddress = ((getDeviceID() < 0x38 )? MPU6050_RA_YA_OFFS_H:0x7A); // MPU6050,MPU9150 Vs MPU6500,MPU9250 + I2Cdev::writeWord(devAddr, SaveAddress, offset, wireObj); +} + +// ZA_OFFS_* register + +int16_t MPU6050_Base::getZAccelOffset() { + uint8_t SaveAddress = ((getDeviceID() < 0x38 )? MPU6050_RA_ZA_OFFS_H:0x7D); // MPU6050,MPU9150 Vs MPU6500,MPU9250 + I2Cdev::readBytes(devAddr, SaveAddress, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +void MPU6050_Base::setZAccelOffset(int16_t offset) { + uint8_t SaveAddress = ((getDeviceID() < 0x38 )? MPU6050_RA_ZA_OFFS_H:0x7D); // MPU6050,MPU9150 Vs MPU6500,MPU9250 + I2Cdev::writeWord(devAddr, SaveAddress, offset, wireObj); +} + +// XG_OFFS_USR* registers + +int16_t MPU6050_Base::getXGyroOffset() { + I2Cdev::readBytes(devAddr, MPU6050_RA_XG_OFFS_USRH, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +void MPU6050_Base::setXGyroOffset(int16_t offset) { + I2Cdev::writeWord(devAddr, MPU6050_RA_XG_OFFS_USRH, offset, wireObj); +} + +// YG_OFFS_USR* register + +int16_t MPU6050_Base::getYGyroOffset() { + I2Cdev::readBytes(devAddr, MPU6050_RA_YG_OFFS_USRH, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +void MPU6050_Base::setYGyroOffset(int16_t offset) { + I2Cdev::writeWord(devAddr, MPU6050_RA_YG_OFFS_USRH, offset, wireObj); +} + +// ZG_OFFS_USR* register + +int16_t MPU6050_Base::getZGyroOffset() { + I2Cdev::readBytes(devAddr, MPU6050_RA_ZG_OFFS_USRH, 2, buffer, I2Cdev::readTimeout, wireObj); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +void MPU6050_Base::setZGyroOffset(int16_t offset) { + I2Cdev::writeWord(devAddr, MPU6050_RA_ZG_OFFS_USRH, offset, wireObj); +} + +// INT_ENABLE register (DMP functions) + +bool MPU6050_Base::getIntPLLReadyEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_PLL_RDY_INT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setIntPLLReadyEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_PLL_RDY_INT_BIT, enabled, wireObj); +} +bool MPU6050_Base::getIntDMPEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DMP_INT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setIntDMPEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DMP_INT_BIT, enabled, wireObj); +} + +// DMP_INT_STATUS + +bool MPU6050_Base::getDMPInt5Status() { + I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_5_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +bool MPU6050_Base::getDMPInt4Status() { + I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_4_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +bool MPU6050_Base::getDMPInt3Status() { + I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_3_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +bool MPU6050_Base::getDMPInt2Status() { + I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_2_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +bool MPU6050_Base::getDMPInt1Status() { + I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_1_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +bool MPU6050_Base::getDMPInt0Status() { + I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_0_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} + +// INT_STATUS register (DMP functions) + +bool MPU6050_Base::getIntPLLReadyStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_PLL_RDY_INT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +bool MPU6050_Base::getIntDMPStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_DMP_INT_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} + +// USER_CTRL register (DMP functions) + +bool MPU6050_Base::getDMPEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_DMP_EN_BIT, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setDMPEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_DMP_EN_BIT, enabled, wireObj); +} +void MPU6050_Base::resetDMP() { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_DMP_RESET_BIT, true, wireObj); +} + +// BANK_SEL register + +void MPU6050_Base::setMemoryBank(uint8_t bank, bool prefetchEnabled, bool userBank) { + bank &= 0x1F; + if (userBank) bank |= 0x20; + if (prefetchEnabled) bank |= 0x40; + I2Cdev::writeByte(devAddr, MPU6050_RA_BANK_SEL, bank, wireObj); +} + +// MEM_START_ADDR register + +void MPU6050_Base::setMemoryStartAddress(uint8_t address) { + I2Cdev::writeByte(devAddr, MPU6050_RA_MEM_START_ADDR, address, wireObj); +} + +// MEM_R_W register + +uint8_t MPU6050_Base::readMemoryByte() { + I2Cdev::readByte(devAddr, MPU6050_RA_MEM_R_W, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::writeMemoryByte(uint8_t data) { + I2Cdev::writeByte(devAddr, MPU6050_RA_MEM_R_W, data, wireObj); +} +void MPU6050_Base::readMemoryBlock(uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address) { + setMemoryBank(bank); + setMemoryStartAddress(address); + uint8_t chunkSize; + for (uint16_t i = 0; i < dataSize;) { + // determine correct chunk size according to bank position and data size + chunkSize = MPU6050_DMP_MEMORY_CHUNK_SIZE; + + // make sure we don't go past the data size + if (i + chunkSize > dataSize) chunkSize = dataSize - i; + + // make sure this chunk doesn't go past the bank boundary (256 bytes) + if (chunkSize > 256 - address) chunkSize = 256 - address; + + // read the chunk of data as specified + I2Cdev::readBytes(devAddr, MPU6050_RA_MEM_R_W, chunkSize, data + i, I2Cdev::readTimeout, wireObj); + + // increase byte index by [chunkSize] + i += chunkSize; + + // uint8_t automatically wraps to 0 at 256 + address += chunkSize; + + // if we aren't done, update bank (if necessary) and address + if (i < dataSize) { + if (address == 0) bank++; + setMemoryBank(bank); + setMemoryStartAddress(address); + } + } +} +bool MPU6050_Base::writeMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address, bool verify, bool useProgMem) { + setMemoryBank(bank); + setMemoryStartAddress(address); + uint8_t chunkSize; + uint8_t *verifyBuffer=0; + uint8_t *progBuffer=0; + uint16_t i; + uint8_t j; + if (verify) verifyBuffer = (uint8_t *)malloc(MPU6050_DMP_MEMORY_CHUNK_SIZE); + if (useProgMem) progBuffer = (uint8_t *)malloc(MPU6050_DMP_MEMORY_CHUNK_SIZE); + for (i = 0; i < dataSize;) { + // determine correct chunk size according to bank position and data size + chunkSize = MPU6050_DMP_MEMORY_CHUNK_SIZE; + + // make sure we don't go past the data size + if (i + chunkSize > dataSize) chunkSize = dataSize - i; + + // make sure this chunk doesn't go past the bank boundary (256 bytes) + if (chunkSize > 256 - address) chunkSize = 256 - address; + + if (useProgMem) { + // write the chunk of data as specified + for (j = 0; j < chunkSize; j++) progBuffer[j] = pgm_read_byte(data + i + j); + } else { + // write the chunk of data as specified + progBuffer = (uint8_t *)data + i; + } + + I2Cdev::writeBytes(devAddr, MPU6050_RA_MEM_R_W, chunkSize, progBuffer, wireObj); + + // verify data if needed + if (verify && verifyBuffer) { + setMemoryBank(bank); + setMemoryStartAddress(address); + I2Cdev::readBytes(devAddr, MPU6050_RA_MEM_R_W, chunkSize, verifyBuffer, I2Cdev::readTimeout, wireObj); + if (memcmp(progBuffer, verifyBuffer, chunkSize) != 0) { + /*Serial.print("Block write verification error, bank "); + Serial.print(bank, DEC); + Serial.print(", address "); + Serial.print(address, DEC); + Serial.print("!\nExpected:"); + for (j = 0; j < chunkSize; j++) { + Serial.print(" 0x"); + if (progBuffer[j] < 16) Serial.print("0"); + Serial.print(progBuffer[j], HEX); + } + Serial.print("\nReceived:"); + for (uint8_t j = 0; j < chunkSize; j++) { + Serial.print(" 0x"); + if (verifyBuffer[i + j] < 16) Serial.print("0"); + Serial.print(verifyBuffer[i + j], HEX); + } + Serial.print("\n");*/ + free(verifyBuffer); + if (useProgMem) free(progBuffer); + return false; // uh oh. + } + } + + // increase byte index by [chunkSize] + i += chunkSize; + + // uint8_t automatically wraps to 0 at 256 + address += chunkSize; + + // if we aren't done, update bank (if necessary) and address + if (i < dataSize) { + if (address == 0) bank++; + setMemoryBank(bank); + setMemoryStartAddress(address); + } + } + if (verify) free(verifyBuffer); + if (useProgMem) free(progBuffer); + return true; +} +bool MPU6050_Base::writeProgMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address, bool verify) { + return writeMemoryBlock(data, dataSize, bank, address, verify, true); +} +bool MPU6050_Base::writeDMPConfigurationSet(const uint8_t *data, uint16_t dataSize, bool useProgMem) { + uint8_t *progBuffer = 0; + uint8_t success, special; + uint16_t i, j; + if (useProgMem) { + progBuffer = (uint8_t *)malloc(8); // assume 8-byte blocks, realloc later if necessary + } + + // config set data is a long string of blocks with the following structure: + // [bank] [offset] [length] [byte[0], byte[1], ..., byte[length]] + uint8_t bank, offset, length; + for (i = 0; i < dataSize;) { + if (useProgMem) { + bank = pgm_read_byte(data + i++); + offset = pgm_read_byte(data + i++); + length = pgm_read_byte(data + i++); + } else { + bank = data[i++]; + offset = data[i++]; + length = data[i++]; + } + + // write data or perform special action + if (length > 0) { + // regular block of data to write + /*Serial.print("Writing config block to bank "); + Serial.print(bank); + Serial.print(", offset "); + Serial.print(offset); + Serial.print(", length="); + Serial.println(length);*/ + if (useProgMem) { + if (sizeof(progBuffer) < length) progBuffer = (uint8_t *)realloc(progBuffer, length); + for (j = 0; j < length; j++) progBuffer[j] = pgm_read_byte(data + i + j); + } else { + progBuffer = (uint8_t *)data + i; + } + success = writeMemoryBlock(progBuffer, length, bank, offset, true); + i += length; + } else { + // special instruction + // NOTE: this kind of behavior (what and when to do certain things) + // is totally undocumented. This code is in here based on observed + // behavior only, and exactly why (or even whether) it has to be here + // is anybody's guess for now. + if (useProgMem) { + special = pgm_read_byte(data + i++); + } else { + special = data[i++]; + } + /*Serial.print("Special command code "); + Serial.print(special, HEX); + Serial.println(" found...");*/ + if (special == 0x01) { + // enable DMP-related interrupts + + //setIntZeroMotionEnabled(true); + //setIntFIFOBufferOverflowEnabled(true); + //setIntDMPEnabled(true); + I2Cdev::writeByte(devAddr, MPU6050_RA_INT_ENABLE, 0x32, wireObj); // single operation + + success = true; + } else { + // unknown special command + success = false; + } + } + + if (!success) { + if (useProgMem) free(progBuffer); + return false; // uh oh + } + } + if (useProgMem) free(progBuffer); + return true; +} +bool MPU6050_Base::writeProgDMPConfigurationSet(const uint8_t *data, uint16_t dataSize) { + return writeDMPConfigurationSet(data, dataSize, true); +} + +// DMP_CFG_1 register + +uint8_t MPU6050_Base::getDMPConfig1() { + I2Cdev::readByte(devAddr, MPU6050_RA_DMP_CFG_1, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setDMPConfig1(uint8_t config) { + I2Cdev::writeByte(devAddr, MPU6050_RA_DMP_CFG_1, config, wireObj); +} + +// DMP_CFG_2 register + +uint8_t MPU6050_Base::getDMPConfig2() { + I2Cdev::readByte(devAddr, MPU6050_RA_DMP_CFG_2, buffer, I2Cdev::readTimeout, wireObj); + return buffer[0]; +} +void MPU6050_Base::setDMPConfig2(uint8_t config) { + I2Cdev::writeByte(devAddr, MPU6050_RA_DMP_CFG_2, config, wireObj); +} + + +//*************************************************************************************** +//********************** Calibration Routines ********************** +//*************************************************************************************** +/** + @brief Fully calibrate Gyro from ZERO in about 6-7 Loops 600-700 readings +*/ +void MPU6050_Base::CalibrateGyro(uint8_t Loops ) { + double kP = 0.3; + double kI = 90; + float x; + x = (100 - map(Loops, 1, 5, 20, 0)) * .01; + kP *= x; + kI *= x; + + PID( 0x43, kP, kI, Loops); +} + +/** + @brief Fully calibrate Accel from ZERO in about 6-7 Loops 600-700 readings +*/ +void MPU6050_Base::CalibrateAccel(uint8_t Loops ) { + + float kP = 0.3; + float kI = 20; + float x; + x = (100 - map(Loops, 1, 5, 20, 0)) * .01; + kP *= x; + kI *= x; + PID( 0x3B, kP, kI, Loops); +} + +void MPU6050_Base::PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops){ + uint8_t SaveAddress = (ReadAddress == 0x3B)?((getDeviceID() < 0x38 )? 0x06:0x77):0x13; + + int16_t Data; + float Reading; + int16_t BitZero[3]; + uint8_t shift =(SaveAddress == 0x77)?3:2; + float Error, PTerm, ITerm[3]; + int16_t eSample; + uint32_t eSum; + uint16_t gravity = 8192; // prevent uninitialized compiler warning + if (ReadAddress == 0x3B) gravity = 16384 >> getFullScaleAccelRange(); + Serial.write('>'); + for (int i = 0; i < 3; i++) { + I2Cdev::readWords(devAddr, SaveAddress + (i * shift), 1, (uint16_t *)&Data, I2Cdev::readTimeout, wireObj); // reads 1 or more 16 bit integers (Word) + Reading = Data; + if(SaveAddress != 0x13){ + BitZero[i] = Data & 1; // Capture Bit Zero to properly handle Accelerometer calibration + ITerm[i] = ((float)Reading) * 8; + } else { + ITerm[i] = Reading * 4; + } + } + for (int L = 0; L < Loops; L++) { + eSample = 0; + for (int c = 0; c < 100; c++) {// 100 PI Calculations + eSum = 0; + for (int i = 0; i < 3; i++) { + I2Cdev::readWords(devAddr, ReadAddress + (i * 2), 1, (uint16_t *)&Data, I2Cdev::readTimeout, wireObj); // reads 1 or more 16 bit integers (Word) + Reading = Data; + if ((ReadAddress == 0x3B)&&(i == 2)) Reading -= gravity; //remove Gravity + Error = -Reading; + eSum += abs(Reading); + PTerm = kP * Error; + ITerm[i] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 + if(SaveAddress != 0x13){ + Data = round((PTerm + ITerm[i] ) / 8); //Compute PID Output + Data = ((Data)&0xFFFE) |BitZero[i]; // Insert Bit0 Saved at beginning + } else Data = round((PTerm + ITerm[i] ) / 4); //Compute PID Output + I2Cdev::writeWords(devAddr, SaveAddress + (i * shift), 1, (uint16_t *)&Data, wireObj); + } + if((c == 99) && eSum > 1000){ // Error is still to great to continue + c = 0; + Serial.write('*'); + } + if((eSum * ((ReadAddress == 0x3B)?.05: 1)) < 5) eSample++; // Successfully found offsets prepare to advance + if((eSum < 100) && (c > 10) && (eSample >= 10)) break; // Advance to next Loop + delay(1); + } + Serial.write('.'); + kP *= .75; + kI *= .75; + for (int i = 0; i < 3; i++){ + if(SaveAddress != 0x13) { + Data = round((ITerm[i] ) / 8); //Compute PID Output + Data = ((Data)&0xFFFE) |BitZero[i]; // Insert Bit0 Saved at beginning + } else Data = round((ITerm[i]) / 4); + I2Cdev::writeWords(devAddr, SaveAddress + (i * shift), 1, (uint16_t *)&Data, wireObj); + } + } + resetFIFO(); + resetDMP(); +} + +int16_t * MPU6050_Base::GetActiveOffsets() { + uint8_t AOffsetRegister = (getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77; + if(AOffsetRegister == 0x06) I2Cdev::readWords(devAddr, AOffsetRegister, 3, (uint16_t *)offsets, I2Cdev::readTimeout, wireObj); + else { + I2Cdev::readWords(devAddr, AOffsetRegister, 1, (uint16_t *)offsets, I2Cdev::readTimeout, wireObj); + I2Cdev::readWords(devAddr, AOffsetRegister+3, 1, (uint16_t *)(offsets+1), I2Cdev::readTimeout, wireObj); + I2Cdev::readWords(devAddr, AOffsetRegister+6, 1, (uint16_t *)(offsets+2), I2Cdev::readTimeout, wireObj); + } + I2Cdev::readWords(devAddr, 0x13, 3, (uint16_t *)(offsets+3), I2Cdev::readTimeout, wireObj); + return offsets; +} + +void MPU6050_Base::PrintActiveOffsets() { + GetActiveOffsets(); + // A_OFFSET_H_READ_A_OFFS(Data); + Serial.print((float)offsets[0], 5); Serial.print(",\t"); + Serial.print((float)offsets[1], 5); Serial.print(",\t"); + Serial.print((float)offsets[2], 5); Serial.print(",\t"); + + // XG_OFFSET_H_READ_OFFS_USR(Data); + Serial.print((float)offsets[3], 5); Serial.print(",\t"); + Serial.print((float)offsets[4], 5); Serial.print(",\t"); + Serial.print((float)offsets[5], 5); Serial.print("\n\n"); +} diff --git a/MPU6050.h b/MPU6050.h new file mode 100644 index 0000000..7cbbb9d --- /dev/null +++ b/MPU6050.h @@ -0,0 +1,852 @@ +// I2Cdev library collection - MPU6050 I2C device class +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 10/3/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021/09/27 - split implementations out of header files, finally +// ... - ongoing debug release + +// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE +// DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF +// YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING. + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#ifndef _MPU6050_H_ +#define _MPU6050_H_ + +#include "I2Cdev.h" +#include "helper_3dmath.h" + +// supporting link: http://forum.arduino.cc/index.php?&topic=143444.msg1079517#msg1079517 +// also: http://forum.arduino.cc/index.php?&topic=141571.msg1062899#msg1062899s + +#ifdef __AVR__ +#include +#elif defined(ESP32) + #include +#else +//#define PROGMEM /* empty */ +//#define pgm_read_byte(x) (*(x)) +//#define pgm_read_word(x) (*(x)) +//#define pgm_read_float(x) (*(x)) +//#define PSTR(STR) STR +#endif + + +#define MPU6050_ADDRESS_AD0_LOW 0x68 // address pin low (GND), default for InvenSense evaluation board +#define MPU6050_ADDRESS_AD0_HIGH 0x69 // address pin high (VCC) +#define MPU6050_DEFAULT_ADDRESS MPU6050_ADDRESS_AD0_LOW + +#define MPU6050_RA_XG_OFFS_TC 0x00 //[7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD +#define MPU6050_RA_YG_OFFS_TC 0x01 //[7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD +#define MPU6050_RA_ZG_OFFS_TC 0x02 //[7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD +#define MPU6050_RA_X_FINE_GAIN 0x03 //[7:0] X_FINE_GAIN +#define MPU6050_RA_Y_FINE_GAIN 0x04 //[7:0] Y_FINE_GAIN +#define MPU6050_RA_Z_FINE_GAIN 0x05 //[7:0] Z_FINE_GAIN +#define MPU6050_RA_XA_OFFS_H 0x06 //[15:0] XA_OFFS +#define MPU6050_RA_XA_OFFS_L_TC 0x07 +#define MPU6050_RA_YA_OFFS_H 0x08 //[15:0] YA_OFFS +#define MPU6050_RA_YA_OFFS_L_TC 0x09 +#define MPU6050_RA_ZA_OFFS_H 0x0A //[15:0] ZA_OFFS +#define MPU6050_RA_ZA_OFFS_L_TC 0x0B +#define MPU6050_RA_SELF_TEST_X 0x0D //[7:5] XA_TEST[4-2], [4:0] XG_TEST[4-0] +#define MPU6050_RA_SELF_TEST_Y 0x0E //[7:5] YA_TEST[4-2], [4:0] YG_TEST[4-0] +#define MPU6050_RA_SELF_TEST_Z 0x0F //[7:5] ZA_TEST[4-2], [4:0] ZG_TEST[4-0] +#define MPU6050_RA_SELF_TEST_A 0x10 //[5:4] XA_TEST[1-0], [3:2] YA_TEST[1-0], [1:0] ZA_TEST[1-0] +#define MPU6050_RA_XG_OFFS_USRH 0x13 //[15:0] XG_OFFS_USR +#define MPU6050_RA_XG_OFFS_USRL 0x14 +#define MPU6050_RA_YG_OFFS_USRH 0x15 //[15:0] YG_OFFS_USR +#define MPU6050_RA_YG_OFFS_USRL 0x16 +#define MPU6050_RA_ZG_OFFS_USRH 0x17 //[15:0] ZG_OFFS_USR +#define MPU6050_RA_ZG_OFFS_USRL 0x18 +#define MPU6050_RA_SMPLRT_DIV 0x19 +#define MPU6050_RA_CONFIG 0x1A +#define MPU6050_RA_GYRO_CONFIG 0x1B +#define MPU6050_RA_ACCEL_CONFIG 0x1C +#define MPU6050_RA_FF_THR 0x1D +#define MPU6050_RA_FF_DUR 0x1E +#define MPU6050_RA_MOT_THR 0x1F +#define MPU6050_RA_MOT_DUR 0x20 +#define MPU6050_RA_ZRMOT_THR 0x21 +#define MPU6050_RA_ZRMOT_DUR 0x22 +#define MPU6050_RA_FIFO_EN 0x23 +#define MPU6050_RA_I2C_MST_CTRL 0x24 +#define MPU6050_RA_I2C_SLV0_ADDR 0x25 +#define MPU6050_RA_I2C_SLV0_REG 0x26 +#define MPU6050_RA_I2C_SLV0_CTRL 0x27 +#define MPU6050_RA_I2C_SLV1_ADDR 0x28 +#define MPU6050_RA_I2C_SLV1_REG 0x29 +#define MPU6050_RA_I2C_SLV1_CTRL 0x2A +#define MPU6050_RA_I2C_SLV2_ADDR 0x2B +#define MPU6050_RA_I2C_SLV2_REG 0x2C +#define MPU6050_RA_I2C_SLV2_CTRL 0x2D +#define MPU6050_RA_I2C_SLV3_ADDR 0x2E +#define MPU6050_RA_I2C_SLV3_REG 0x2F +#define MPU6050_RA_I2C_SLV3_CTRL 0x30 +#define MPU6050_RA_I2C_SLV4_ADDR 0x31 +#define MPU6050_RA_I2C_SLV4_REG 0x32 +#define MPU6050_RA_I2C_SLV4_DO 0x33 +#define MPU6050_RA_I2C_SLV4_CTRL 0x34 +#define MPU6050_RA_I2C_SLV4_DI 0x35 +#define MPU6050_RA_I2C_MST_STATUS 0x36 +#define MPU6050_RA_INT_PIN_CFG 0x37 +#define MPU6050_RA_INT_ENABLE 0x38 +#define MPU6050_RA_DMP_INT_STATUS 0x39 +#define MPU6050_RA_INT_STATUS 0x3A +#define MPU6050_RA_ACCEL_XOUT_H 0x3B +#define MPU6050_RA_ACCEL_XOUT_L 0x3C +#define MPU6050_RA_ACCEL_YOUT_H 0x3D +#define MPU6050_RA_ACCEL_YOUT_L 0x3E +#define MPU6050_RA_ACCEL_ZOUT_H 0x3F +#define MPU6050_RA_ACCEL_ZOUT_L 0x40 +#define MPU6050_RA_TEMP_OUT_H 0x41 +#define MPU6050_RA_TEMP_OUT_L 0x42 +#define MPU6050_RA_GYRO_XOUT_H 0x43 +#define MPU6050_RA_GYRO_XOUT_L 0x44 +#define MPU6050_RA_GYRO_YOUT_H 0x45 +#define MPU6050_RA_GYRO_YOUT_L 0x46 +#define MPU6050_RA_GYRO_ZOUT_H 0x47 +#define MPU6050_RA_GYRO_ZOUT_L 0x48 +#define MPU6050_RA_EXT_SENS_DATA_00 0x49 +#define MPU6050_RA_EXT_SENS_DATA_01 0x4A +#define MPU6050_RA_EXT_SENS_DATA_02 0x4B +#define MPU6050_RA_EXT_SENS_DATA_03 0x4C +#define MPU6050_RA_EXT_SENS_DATA_04 0x4D +#define MPU6050_RA_EXT_SENS_DATA_05 0x4E +#define MPU6050_RA_EXT_SENS_DATA_06 0x4F +#define MPU6050_RA_EXT_SENS_DATA_07 0x50 +#define MPU6050_RA_EXT_SENS_DATA_08 0x51 +#define MPU6050_RA_EXT_SENS_DATA_09 0x52 +#define MPU6050_RA_EXT_SENS_DATA_10 0x53 +#define MPU6050_RA_EXT_SENS_DATA_11 0x54 +#define MPU6050_RA_EXT_SENS_DATA_12 0x55 +#define MPU6050_RA_EXT_SENS_DATA_13 0x56 +#define MPU6050_RA_EXT_SENS_DATA_14 0x57 +#define MPU6050_RA_EXT_SENS_DATA_15 0x58 +#define MPU6050_RA_EXT_SENS_DATA_16 0x59 +#define MPU6050_RA_EXT_SENS_DATA_17 0x5A +#define MPU6050_RA_EXT_SENS_DATA_18 0x5B +#define MPU6050_RA_EXT_SENS_DATA_19 0x5C +#define MPU6050_RA_EXT_SENS_DATA_20 0x5D +#define MPU6050_RA_EXT_SENS_DATA_21 0x5E +#define MPU6050_RA_EXT_SENS_DATA_22 0x5F +#define MPU6050_RA_EXT_SENS_DATA_23 0x60 +#define MPU6050_RA_MOT_DETECT_STATUS 0x61 +#define MPU6050_RA_I2C_SLV0_DO 0x63 +#define MPU6050_RA_I2C_SLV1_DO 0x64 +#define MPU6050_RA_I2C_SLV2_DO 0x65 +#define MPU6050_RA_I2C_SLV3_DO 0x66 +#define MPU6050_RA_I2C_MST_DELAY_CTRL 0x67 +#define MPU6050_RA_SIGNAL_PATH_RESET 0x68 +#define MPU6050_RA_MOT_DETECT_CTRL 0x69 +#define MPU6050_RA_USER_CTRL 0x6A +#define MPU6050_RA_PWR_MGMT_1 0x6B +#define MPU6050_RA_PWR_MGMT_2 0x6C +#define MPU6050_RA_BANK_SEL 0x6D +#define MPU6050_RA_MEM_START_ADDR 0x6E +#define MPU6050_RA_MEM_R_W 0x6F +#define MPU6050_RA_DMP_CFG_1 0x70 +#define MPU6050_RA_DMP_CFG_2 0x71 +#define MPU6050_RA_FIFO_COUNTH 0x72 +#define MPU6050_RA_FIFO_COUNTL 0x73 +#define MPU6050_RA_FIFO_R_W 0x74 +#define MPU6050_RA_WHO_AM_I 0x75 + +#define MPU6050_SELF_TEST_XA_1_BIT 0x07 +#define MPU6050_SELF_TEST_XA_1_LENGTH 0x03 +#define MPU6050_SELF_TEST_XA_2_BIT 0x05 +#define MPU6050_SELF_TEST_XA_2_LENGTH 0x02 +#define MPU6050_SELF_TEST_YA_1_BIT 0x07 +#define MPU6050_SELF_TEST_YA_1_LENGTH 0x03 +#define MPU6050_SELF_TEST_YA_2_BIT 0x03 +#define MPU6050_SELF_TEST_YA_2_LENGTH 0x02 +#define MPU6050_SELF_TEST_ZA_1_BIT 0x07 +#define MPU6050_SELF_TEST_ZA_1_LENGTH 0x03 +#define MPU6050_SELF_TEST_ZA_2_BIT 0x01 +#define MPU6050_SELF_TEST_ZA_2_LENGTH 0x02 + +#define MPU6050_SELF_TEST_XG_1_BIT 0x04 +#define MPU6050_SELF_TEST_XG_1_LENGTH 0x05 +#define MPU6050_SELF_TEST_YG_1_BIT 0x04 +#define MPU6050_SELF_TEST_YG_1_LENGTH 0x05 +#define MPU6050_SELF_TEST_ZG_1_BIT 0x04 +#define MPU6050_SELF_TEST_ZG_1_LENGTH 0x05 + +#define MPU6050_TC_PWR_MODE_BIT 7 +#define MPU6050_TC_OFFSET_BIT 6 +#define MPU6050_TC_OFFSET_LENGTH 6 +#define MPU6050_TC_OTP_BNK_VLD_BIT 0 + +#define MPU6050_VDDIO_LEVEL_VLOGIC 0 +#define MPU6050_VDDIO_LEVEL_VDD 1 + +#define MPU6050_CFG_EXT_SYNC_SET_BIT 5 +#define MPU6050_CFG_EXT_SYNC_SET_LENGTH 3 +#define MPU6050_CFG_DLPF_CFG_BIT 2 +#define MPU6050_CFG_DLPF_CFG_LENGTH 3 + +#define MPU6050_EXT_SYNC_DISABLED 0x0 +#define MPU6050_EXT_SYNC_TEMP_OUT_L 0x1 +#define MPU6050_EXT_SYNC_GYRO_XOUT_L 0x2 +#define MPU6050_EXT_SYNC_GYRO_YOUT_L 0x3 +#define MPU6050_EXT_SYNC_GYRO_ZOUT_L 0x4 +#define MPU6050_EXT_SYNC_ACCEL_XOUT_L 0x5 +#define MPU6050_EXT_SYNC_ACCEL_YOUT_L 0x6 +#define MPU6050_EXT_SYNC_ACCEL_ZOUT_L 0x7 + +#define MPU6050_DLPF_BW_256 0x00 +#define MPU6050_DLPF_BW_188 0x01 +#define MPU6050_DLPF_BW_98 0x02 +#define MPU6050_DLPF_BW_42 0x03 +#define MPU6050_DLPF_BW_20 0x04 +#define MPU6050_DLPF_BW_10 0x05 +#define MPU6050_DLPF_BW_5 0x06 + +#define MPU6050_GCONFIG_FS_SEL_BIT 4 +#define MPU6050_GCONFIG_FS_SEL_LENGTH 2 + +#define MPU6050_GYRO_FS_250 0x00 +#define MPU6050_GYRO_FS_500 0x01 +#define MPU6050_GYRO_FS_1000 0x02 +#define MPU6050_GYRO_FS_2000 0x03 + +#define MPU6050_ACONFIG_XA_ST_BIT 7 +#define MPU6050_ACONFIG_YA_ST_BIT 6 +#define MPU6050_ACONFIG_ZA_ST_BIT 5 +#define MPU6050_ACONFIG_AFS_SEL_BIT 4 +#define MPU6050_ACONFIG_AFS_SEL_LENGTH 2 +#define MPU6050_ACONFIG_ACCEL_HPF_BIT 2 +#define MPU6050_ACONFIG_ACCEL_HPF_LENGTH 3 + +#define MPU6050_ACCEL_FS_2 0x00 +#define MPU6050_ACCEL_FS_4 0x01 +#define MPU6050_ACCEL_FS_8 0x02 +#define MPU6050_ACCEL_FS_16 0x03 + +#define MPU6050_DHPF_RESET 0x00 +#define MPU6050_DHPF_5 0x01 +#define MPU6050_DHPF_2P5 0x02 +#define MPU6050_DHPF_1P25 0x03 +#define MPU6050_DHPF_0P63 0x04 +#define MPU6050_DHPF_HOLD 0x07 + +#define MPU6050_TEMP_FIFO_EN_BIT 7 +#define MPU6050_XG_FIFO_EN_BIT 6 +#define MPU6050_YG_FIFO_EN_BIT 5 +#define MPU6050_ZG_FIFO_EN_BIT 4 +#define MPU6050_ACCEL_FIFO_EN_BIT 3 +#define MPU6050_SLV2_FIFO_EN_BIT 2 +#define MPU6050_SLV1_FIFO_EN_BIT 1 +#define MPU6050_SLV0_FIFO_EN_BIT 0 + +#define MPU6050_MULT_MST_EN_BIT 7 +#define MPU6050_WAIT_FOR_ES_BIT 6 +#define MPU6050_SLV_3_FIFO_EN_BIT 5 +#define MPU6050_I2C_MST_P_NSR_BIT 4 +#define MPU6050_I2C_MST_CLK_BIT 3 +#define MPU6050_I2C_MST_CLK_LENGTH 4 + +#define MPU6050_CLOCK_DIV_348 0x0 +#define MPU6050_CLOCK_DIV_333 0x1 +#define MPU6050_CLOCK_DIV_320 0x2 +#define MPU6050_CLOCK_DIV_308 0x3 +#define MPU6050_CLOCK_DIV_296 0x4 +#define MPU6050_CLOCK_DIV_286 0x5 +#define MPU6050_CLOCK_DIV_276 0x6 +#define MPU6050_CLOCK_DIV_267 0x7 +#define MPU6050_CLOCK_DIV_258 0x8 +#define MPU6050_CLOCK_DIV_500 0x9 +#define MPU6050_CLOCK_DIV_471 0xA +#define MPU6050_CLOCK_DIV_444 0xB +#define MPU6050_CLOCK_DIV_421 0xC +#define MPU6050_CLOCK_DIV_400 0xD +#define MPU6050_CLOCK_DIV_381 0xE +#define MPU6050_CLOCK_DIV_364 0xF + +#define MPU6050_I2C_SLV_RW_BIT 7 +#define MPU6050_I2C_SLV_ADDR_BIT 6 +#define MPU6050_I2C_SLV_ADDR_LENGTH 7 +#define MPU6050_I2C_SLV_EN_BIT 7 +#define MPU6050_I2C_SLV_BYTE_SW_BIT 6 +#define MPU6050_I2C_SLV_REG_DIS_BIT 5 +#define MPU6050_I2C_SLV_GRP_BIT 4 +#define MPU6050_I2C_SLV_LEN_BIT 3 +#define MPU6050_I2C_SLV_LEN_LENGTH 4 + +#define MPU6050_I2C_SLV4_RW_BIT 7 +#define MPU6050_I2C_SLV4_ADDR_BIT 6 +#define MPU6050_I2C_SLV4_ADDR_LENGTH 7 +#define MPU6050_I2C_SLV4_EN_BIT 7 +#define MPU6050_I2C_SLV4_INT_EN_BIT 6 +#define MPU6050_I2C_SLV4_REG_DIS_BIT 5 +#define MPU6050_I2C_SLV4_MST_DLY_BIT 4 +#define MPU6050_I2C_SLV4_MST_DLY_LENGTH 5 + +#define MPU6050_MST_PASS_THROUGH_BIT 7 +#define MPU6050_MST_I2C_SLV4_DONE_BIT 6 +#define MPU6050_MST_I2C_LOST_ARB_BIT 5 +#define MPU6050_MST_I2C_SLV4_NACK_BIT 4 +#define MPU6050_MST_I2C_SLV3_NACK_BIT 3 +#define MPU6050_MST_I2C_SLV2_NACK_BIT 2 +#define MPU6050_MST_I2C_SLV1_NACK_BIT 1 +#define MPU6050_MST_I2C_SLV0_NACK_BIT 0 + +#define MPU6050_INTCFG_INT_LEVEL_BIT 7 +#define MPU6050_INTCFG_INT_OPEN_BIT 6 +#define MPU6050_INTCFG_LATCH_INT_EN_BIT 5 +#define MPU6050_INTCFG_INT_RD_CLEAR_BIT 4 +#define MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT 3 +#define MPU6050_INTCFG_FSYNC_INT_EN_BIT 2 +#define MPU6050_INTCFG_I2C_BYPASS_EN_BIT 1 +#define MPU6050_INTCFG_CLKOUT_EN_BIT 0 + +#define MPU6050_INTMODE_ACTIVEHIGH 0x00 +#define MPU6050_INTMODE_ACTIVELOW 0x01 + +#define MPU6050_INTDRV_PUSHPULL 0x00 +#define MPU6050_INTDRV_OPENDRAIN 0x01 + +#define MPU6050_INTLATCH_50USPULSE 0x00 +#define MPU6050_INTLATCH_WAITCLEAR 0x01 + +#define MPU6050_INTCLEAR_STATUSREAD 0x00 +#define MPU6050_INTCLEAR_ANYREAD 0x01 + +#define MPU6050_INTERRUPT_FF_BIT 7 +#define MPU6050_INTERRUPT_MOT_BIT 6 +#define MPU6050_INTERRUPT_ZMOT_BIT 5 +#define MPU6050_INTERRUPT_FIFO_OFLOW_BIT 4 +#define MPU6050_INTERRUPT_I2C_MST_INT_BIT 3 +#define MPU6050_INTERRUPT_PLL_RDY_INT_BIT 2 +#define MPU6050_INTERRUPT_DMP_INT_BIT 1 +#define MPU6050_INTERRUPT_DATA_RDY_BIT 0 + +// TODO: figure out what these actually do +// UMPL source code is not very obivous +#define MPU6050_DMPINT_5_BIT 5 +#define MPU6050_DMPINT_4_BIT 4 +#define MPU6050_DMPINT_3_BIT 3 +#define MPU6050_DMPINT_2_BIT 2 +#define MPU6050_DMPINT_1_BIT 1 +#define MPU6050_DMPINT_0_BIT 0 + +#define MPU6050_MOTION_MOT_XNEG_BIT 7 +#define MPU6050_MOTION_MOT_XPOS_BIT 6 +#define MPU6050_MOTION_MOT_YNEG_BIT 5 +#define MPU6050_MOTION_MOT_YPOS_BIT 4 +#define MPU6050_MOTION_MOT_ZNEG_BIT 3 +#define MPU6050_MOTION_MOT_ZPOS_BIT 2 +#define MPU6050_MOTION_MOT_ZRMOT_BIT 0 + +#define MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT 7 +#define MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT 4 +#define MPU6050_DELAYCTRL_I2C_SLV3_DLY_EN_BIT 3 +#define MPU6050_DELAYCTRL_I2C_SLV2_DLY_EN_BIT 2 +#define MPU6050_DELAYCTRL_I2C_SLV1_DLY_EN_BIT 1 +#define MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT 0 + +#define MPU6050_PATHRESET_GYRO_RESET_BIT 2 +#define MPU6050_PATHRESET_ACCEL_RESET_BIT 1 +#define MPU6050_PATHRESET_TEMP_RESET_BIT 0 + +#define MPU6050_DETECT_ACCEL_ON_DELAY_BIT 5 +#define MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH 2 +#define MPU6050_DETECT_FF_COUNT_BIT 3 +#define MPU6050_DETECT_FF_COUNT_LENGTH 2 +#define MPU6050_DETECT_MOT_COUNT_BIT 1 +#define MPU6050_DETECT_MOT_COUNT_LENGTH 2 + +#define MPU6050_DETECT_DECREMENT_RESET 0x0 +#define MPU6050_DETECT_DECREMENT_1 0x1 +#define MPU6050_DETECT_DECREMENT_2 0x2 +#define MPU6050_DETECT_DECREMENT_4 0x3 + +#define MPU6050_USERCTRL_DMP_EN_BIT 7 +#define MPU6050_USERCTRL_FIFO_EN_BIT 6 +#define MPU6050_USERCTRL_I2C_MST_EN_BIT 5 +#define MPU6050_USERCTRL_I2C_IF_DIS_BIT 4 +#define MPU6050_USERCTRL_DMP_RESET_BIT 3 +#define MPU6050_USERCTRL_FIFO_RESET_BIT 2 +#define MPU6050_USERCTRL_I2C_MST_RESET_BIT 1 +#define MPU6050_USERCTRL_SIG_COND_RESET_BIT 0 + +#define MPU6050_PWR1_DEVICE_RESET_BIT 7 +#define MPU6050_PWR1_SLEEP_BIT 6 +#define MPU6050_PWR1_CYCLE_BIT 5 +#define MPU6050_PWR1_TEMP_DIS_BIT 3 +#define MPU6050_PWR1_CLKSEL_BIT 2 +#define MPU6050_PWR1_CLKSEL_LENGTH 3 + +#define MPU6050_CLOCK_INTERNAL 0x00 +#define MPU6050_CLOCK_PLL_XGYRO 0x01 +#define MPU6050_CLOCK_PLL_YGYRO 0x02 +#define MPU6050_CLOCK_PLL_ZGYRO 0x03 +#define MPU6050_CLOCK_PLL_EXT32K 0x04 +#define MPU6050_CLOCK_PLL_EXT19M 0x05 +#define MPU6050_CLOCK_KEEP_RESET 0x07 + +#define MPU6050_PWR2_LP_WAKE_CTRL_BIT 7 +#define MPU6050_PWR2_LP_WAKE_CTRL_LENGTH 2 +#define MPU6050_PWR2_STBY_XA_BIT 5 +#define MPU6050_PWR2_STBY_YA_BIT 4 +#define MPU6050_PWR2_STBY_ZA_BIT 3 +#define MPU6050_PWR2_STBY_XG_BIT 2 +#define MPU6050_PWR2_STBY_YG_BIT 1 +#define MPU6050_PWR2_STBY_ZG_BIT 0 + +#define MPU6050_WAKE_FREQ_1P25 0x0 +#define MPU6050_WAKE_FREQ_2P5 0x1 +#define MPU6050_WAKE_FREQ_5 0x2 +#define MPU6050_WAKE_FREQ_10 0x3 + +#define MPU6050_BANKSEL_PRFTCH_EN_BIT 6 +#define MPU6050_BANKSEL_CFG_USER_BANK_BIT 5 +#define MPU6050_BANKSEL_MEM_SEL_BIT 4 +#define MPU6050_BANKSEL_MEM_SEL_LENGTH 5 + +#define MPU6050_WHO_AM_I_BIT 6 +#define MPU6050_WHO_AM_I_LENGTH 6 + +#define MPU6050_DMP_MEMORY_BANKS 8 +#define MPU6050_DMP_MEMORY_BANK_SIZE 256 +#define MPU6050_DMP_MEMORY_CHUNK_SIZE 16 + +#define MPU6050_FIFO_DEFAULT_TIMEOUT 11000 + +class MPU6050_Base { + public: + MPU6050_Base(uint8_t address=MPU6050_DEFAULT_ADDRESS, void *wireObj=0); + + void initialize(); + bool testConnection(); + + // AUX_VDDIO register + uint8_t getAuxVDDIOLevel(); + void setAuxVDDIOLevel(uint8_t level); + + // SMPLRT_DIV register + uint8_t getRate(); + void setRate(uint8_t rate); + + // CONFIG register + uint8_t getExternalFrameSync(); + void setExternalFrameSync(uint8_t sync); + uint8_t getDLPFMode(); + void setDLPFMode(uint8_t bandwidth); + + // GYRO_CONFIG register + uint8_t getFullScaleGyroRange(); + void setFullScaleGyroRange(uint8_t range); + + // SELF_TEST registers + uint8_t getAccelXSelfTestFactoryTrim(); + uint8_t getAccelYSelfTestFactoryTrim(); + uint8_t getAccelZSelfTestFactoryTrim(); + + uint8_t getGyroXSelfTestFactoryTrim(); + uint8_t getGyroYSelfTestFactoryTrim(); + uint8_t getGyroZSelfTestFactoryTrim(); + + // ACCEL_CONFIG register + bool getAccelXSelfTest(); + void setAccelXSelfTest(bool enabled); + bool getAccelYSelfTest(); + void setAccelYSelfTest(bool enabled); + bool getAccelZSelfTest(); + void setAccelZSelfTest(bool enabled); + uint8_t getFullScaleAccelRange(); + void setFullScaleAccelRange(uint8_t range); + uint8_t getDHPFMode(); + void setDHPFMode(uint8_t mode); + + // FF_THR register + uint8_t getFreefallDetectionThreshold(); + void setFreefallDetectionThreshold(uint8_t threshold); + + // FF_DUR register + uint8_t getFreefallDetectionDuration(); + void setFreefallDetectionDuration(uint8_t duration); + + // MOT_THR register + uint8_t getMotionDetectionThreshold(); + void setMotionDetectionThreshold(uint8_t threshold); + + // MOT_DUR register + uint8_t getMotionDetectionDuration(); + void setMotionDetectionDuration(uint8_t duration); + + // ZRMOT_THR register + uint8_t getZeroMotionDetectionThreshold(); + void setZeroMotionDetectionThreshold(uint8_t threshold); + + // ZRMOT_DUR register + uint8_t getZeroMotionDetectionDuration(); + void setZeroMotionDetectionDuration(uint8_t duration); + + // FIFO_EN register + bool getTempFIFOEnabled(); + void setTempFIFOEnabled(bool enabled); + bool getXGyroFIFOEnabled(); + void setXGyroFIFOEnabled(bool enabled); + bool getYGyroFIFOEnabled(); + void setYGyroFIFOEnabled(bool enabled); + bool getZGyroFIFOEnabled(); + void setZGyroFIFOEnabled(bool enabled); + bool getAccelFIFOEnabled(); + void setAccelFIFOEnabled(bool enabled); + bool getSlave2FIFOEnabled(); + void setSlave2FIFOEnabled(bool enabled); + bool getSlave1FIFOEnabled(); + void setSlave1FIFOEnabled(bool enabled); + bool getSlave0FIFOEnabled(); + void setSlave0FIFOEnabled(bool enabled); + + // I2C_MST_CTRL register + bool getMultiMasterEnabled(); + void setMultiMasterEnabled(bool enabled); + bool getWaitForExternalSensorEnabled(); + void setWaitForExternalSensorEnabled(bool enabled); + bool getSlave3FIFOEnabled(); + void setSlave3FIFOEnabled(bool enabled); + bool getSlaveReadWriteTransitionEnabled(); + void setSlaveReadWriteTransitionEnabled(bool enabled); + uint8_t getMasterClockSpeed(); + void setMasterClockSpeed(uint8_t speed); + + // I2C_SLV* registers (Slave 0-3) + uint8_t getSlaveAddress(uint8_t num); + void setSlaveAddress(uint8_t num, uint8_t address); + uint8_t getSlaveRegister(uint8_t num); + void setSlaveRegister(uint8_t num, uint8_t reg); + bool getSlaveEnabled(uint8_t num); + void setSlaveEnabled(uint8_t num, bool enabled); + bool getSlaveWordByteSwap(uint8_t num); + void setSlaveWordByteSwap(uint8_t num, bool enabled); + bool getSlaveWriteMode(uint8_t num); + void setSlaveWriteMode(uint8_t num, bool mode); + bool getSlaveWordGroupOffset(uint8_t num); + void setSlaveWordGroupOffset(uint8_t num, bool enabled); + uint8_t getSlaveDataLength(uint8_t num); + void setSlaveDataLength(uint8_t num, uint8_t length); + + // I2C_SLV* registers (Slave 4) + uint8_t getSlave4Address(); + void setSlave4Address(uint8_t address); + uint8_t getSlave4Register(); + void setSlave4Register(uint8_t reg); + void setSlave4OutputByte(uint8_t data); + bool getSlave4Enabled(); + void setSlave4Enabled(bool enabled); + bool getSlave4InterruptEnabled(); + void setSlave4InterruptEnabled(bool enabled); + bool getSlave4WriteMode(); + void setSlave4WriteMode(bool mode); + uint8_t getSlave4MasterDelay(); + void setSlave4MasterDelay(uint8_t delay); + uint8_t getSlate4InputByte(); + + // I2C_MST_STATUS register + bool getPassthroughStatus(); + bool getSlave4IsDone(); + bool getLostArbitration(); + bool getSlave4Nack(); + bool getSlave3Nack(); + bool getSlave2Nack(); + bool getSlave1Nack(); + bool getSlave0Nack(); + + // INT_PIN_CFG register + bool getInterruptMode(); + void setInterruptMode(bool mode); + bool getInterruptDrive(); + void setInterruptDrive(bool drive); + bool getInterruptLatch(); + void setInterruptLatch(bool latch); + bool getInterruptLatchClear(); + void setInterruptLatchClear(bool clear); + bool getFSyncInterruptLevel(); + void setFSyncInterruptLevel(bool level); + bool getFSyncInterruptEnabled(); + void setFSyncInterruptEnabled(bool enabled); + bool getI2CBypassEnabled(); + void setI2CBypassEnabled(bool enabled); + bool getClockOutputEnabled(); + void setClockOutputEnabled(bool enabled); + + // INT_ENABLE register + uint8_t getIntEnabled(); + void setIntEnabled(uint8_t enabled); + bool getIntFreefallEnabled(); + void setIntFreefallEnabled(bool enabled); + bool getIntMotionEnabled(); + void setIntMotionEnabled(bool enabled); + bool getIntZeroMotionEnabled(); + void setIntZeroMotionEnabled(bool enabled); + bool getIntFIFOBufferOverflowEnabled(); + void setIntFIFOBufferOverflowEnabled(bool enabled); + bool getIntI2CMasterEnabled(); + void setIntI2CMasterEnabled(bool enabled); + bool getIntDataReadyEnabled(); + void setIntDataReadyEnabled(bool enabled); + + // INT_STATUS register + uint8_t getIntStatus(); + bool getIntFreefallStatus(); + bool getIntMotionStatus(); + bool getIntZeroMotionStatus(); + bool getIntFIFOBufferOverflowStatus(); + bool getIntI2CMasterStatus(); + bool getIntDataReadyStatus(); + + // ACCEL_*OUT_* registers + void getMotion9(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz, int16_t* mx, int16_t* my, int16_t* mz); + void getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz); + void getAcceleration(int16_t* x, int16_t* y, int16_t* z); + int16_t getAccelerationX(); + int16_t getAccelerationY(); + int16_t getAccelerationZ(); + + // TEMP_OUT_* registers + int16_t getTemperature(); + + // GYRO_*OUT_* registers + void getRotation(int16_t* x, int16_t* y, int16_t* z); + int16_t getRotationX(); + int16_t getRotationY(); + int16_t getRotationZ(); + + // EXT_SENS_DATA_* registers + uint8_t getExternalSensorByte(int position); + uint16_t getExternalSensorWord(int position); + uint32_t getExternalSensorDWord(int position); + + // MOT_DETECT_STATUS register + uint8_t getMotionStatus(); + bool getXNegMotionDetected(); + bool getXPosMotionDetected(); + bool getYNegMotionDetected(); + bool getYPosMotionDetected(); + bool getZNegMotionDetected(); + bool getZPosMotionDetected(); + bool getZeroMotionDetected(); + + // I2C_SLV*_DO register + void setSlaveOutputByte(uint8_t num, uint8_t data); + + // I2C_MST_DELAY_CTRL register + bool getExternalShadowDelayEnabled(); + void setExternalShadowDelayEnabled(bool enabled); + bool getSlaveDelayEnabled(uint8_t num); + void setSlaveDelayEnabled(uint8_t num, bool enabled); + + // SIGNAL_PATH_RESET register + void resetGyroscopePath(); + void resetAccelerometerPath(); + void resetTemperaturePath(); + + // MOT_DETECT_CTRL register + uint8_t getAccelerometerPowerOnDelay(); + void setAccelerometerPowerOnDelay(uint8_t delay); + uint8_t getFreefallDetectionCounterDecrement(); + void setFreefallDetectionCounterDecrement(uint8_t decrement); + uint8_t getMotionDetectionCounterDecrement(); + void setMotionDetectionCounterDecrement(uint8_t decrement); + + // USER_CTRL register + bool getFIFOEnabled(); + void setFIFOEnabled(bool enabled); + bool getI2CMasterModeEnabled(); + void setI2CMasterModeEnabled(bool enabled); + void switchSPIEnabled(bool enabled); + void resetFIFO(); + void resetI2CMaster(); + void resetSensors(); + + // PWR_MGMT_1 register + void reset(); + bool getSleepEnabled(); + void setSleepEnabled(bool enabled); + bool getWakeCycleEnabled(); + void setWakeCycleEnabled(bool enabled); + bool getTempSensorEnabled(); + void setTempSensorEnabled(bool enabled); + uint8_t getClockSource(); + void setClockSource(uint8_t source); + + // PWR_MGMT_2 register + uint8_t getWakeFrequency(); + void setWakeFrequency(uint8_t frequency); + bool getStandbyXAccelEnabled(); + void setStandbyXAccelEnabled(bool enabled); + bool getStandbyYAccelEnabled(); + void setStandbyYAccelEnabled(bool enabled); + bool getStandbyZAccelEnabled(); + void setStandbyZAccelEnabled(bool enabled); + bool getStandbyXGyroEnabled(); + void setStandbyXGyroEnabled(bool enabled); + bool getStandbyYGyroEnabled(); + void setStandbyYGyroEnabled(bool enabled); + bool getStandbyZGyroEnabled(); + void setStandbyZGyroEnabled(bool enabled); + + // FIFO_COUNT_* registers + uint16_t getFIFOCount(); + + // FIFO_R_W register + uint8_t getFIFOByte(); + int8_t GetCurrentFIFOPacket(uint8_t *data, uint8_t length); + void setFIFOByte(uint8_t data); + void getFIFOBytes(uint8_t *data, uint8_t length); + void setFIFOTimeout(uint32_t fifoTimeout); + uint32_t getFIFOTimeout(); + + // WHO_AM_I register + uint8_t getDeviceID(); + void setDeviceID(uint8_t id); + + // ======== UNDOCUMENTED/DMP REGISTERS/METHODS ======== + + // XG_OFFS_TC register + uint8_t getOTPBankValid(); + void setOTPBankValid(bool enabled); + int8_t getXGyroOffsetTC(); + void setXGyroOffsetTC(int8_t offset); + + // YG_OFFS_TC register + int8_t getYGyroOffsetTC(); + void setYGyroOffsetTC(int8_t offset); + + // ZG_OFFS_TC register + int8_t getZGyroOffsetTC(); + void setZGyroOffsetTC(int8_t offset); + + // X_FINE_GAIN register + int8_t getXFineGain(); + void setXFineGain(int8_t gain); + + // Y_FINE_GAIN register + int8_t getYFineGain(); + void setYFineGain(int8_t gain); + + // Z_FINE_GAIN register + int8_t getZFineGain(); + void setZFineGain(int8_t gain); + + // XA_OFFS_* registers + int16_t getXAccelOffset(); + void setXAccelOffset(int16_t offset); + + // YA_OFFS_* register + int16_t getYAccelOffset(); + void setYAccelOffset(int16_t offset); + + // ZA_OFFS_* register + int16_t getZAccelOffset(); + void setZAccelOffset(int16_t offset); + + // XG_OFFS_USR* registers + int16_t getXGyroOffset(); + void setXGyroOffset(int16_t offset); + + // YG_OFFS_USR* register + int16_t getYGyroOffset(); + void setYGyroOffset(int16_t offset); + + // ZG_OFFS_USR* register + int16_t getZGyroOffset(); + void setZGyroOffset(int16_t offset); + + // INT_ENABLE register (DMP functions) + bool getIntPLLReadyEnabled(); + void setIntPLLReadyEnabled(bool enabled); + bool getIntDMPEnabled(); + void setIntDMPEnabled(bool enabled); + + // DMP_INT_STATUS + bool getDMPInt5Status(); + bool getDMPInt4Status(); + bool getDMPInt3Status(); + bool getDMPInt2Status(); + bool getDMPInt1Status(); + bool getDMPInt0Status(); + + // INT_STATUS register (DMP functions) + bool getIntPLLReadyStatus(); + bool getIntDMPStatus(); + + // USER_CTRL register (DMP functions) + bool getDMPEnabled(); + void setDMPEnabled(bool enabled); + void resetDMP(); + + // BANK_SEL register + void setMemoryBank(uint8_t bank, bool prefetchEnabled=false, bool userBank=false); + + // MEM_START_ADDR register + void setMemoryStartAddress(uint8_t address); + + // MEM_R_W register + uint8_t readMemoryByte(); + void writeMemoryByte(uint8_t data); + void readMemoryBlock(uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0); + bool writeMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0, bool verify=true, bool useProgMem=false); + bool writeProgMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0, bool verify=true); + + bool writeDMPConfigurationSet(const uint8_t *data, uint16_t dataSize, bool useProgMem=false); + bool writeProgDMPConfigurationSet(const uint8_t *data, uint16_t dataSize); + + // DMP_CFG_1 register + uint8_t getDMPConfig1(); + void setDMPConfig1(uint8_t config); + + // DMP_CFG_2 register + uint8_t getDMPConfig2(); + void setDMPConfig2(uint8_t config); + + // Calibration Routines + void CalibrateGyro(uint8_t Loops = 15); // Fine tune after setting offsets with less Loops. + void CalibrateAccel(uint8_t Loops = 15);// Fine tune after setting offsets with less Loops. + void PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops); // Does the math + void PrintActiveOffsets(); // See the results of the Calibration + int16_t * GetActiveOffsets(); + + protected: + uint8_t devAddr; + void *wireObj; + uint8_t buffer[14]; + uint32_t fifoTimeout = MPU6050_FIFO_DEFAULT_TIMEOUT; + + private: + int16_t offsets[6]; +}; + +#ifndef I2CDEVLIB_MPU6050_TYPEDEF +#define I2CDEVLIB_MPU6050_TYPEDEF +typedef MPU6050_Base MPU6050; +#endif + +#endif /* _MPU6050_H_ */ diff --git a/MPU6050_6Axis_MotionApps20.cpp b/MPU6050_6Axis_MotionApps20.cpp new file mode 100644 index 0000000..3a89674 --- /dev/null +++ b/MPU6050_6Axis_MotionApps20.cpp @@ -0,0 +1,616 @@ +// I2Cdev library collection - MPU6050 I2C device class, 6-axis MotionApps 2.0 implementation +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 5/20/2013 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021/09/27 - split implementations out of header files, finally +// 2019/07/08 - merged all DMP Firmware configuration items into the dmpMemory array +// - Simplified dmpInitialize() to accomidate the dmpmemory array alterations + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2021 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +// MotionApps 2.0 DMP implementation, built using the MPU-6050EVB evaluation board +#define MPU6050_INCLUDE_DMP_MOTIONAPPS20 + +#include "MPU6050_6Axis_MotionApps20.h" + +// Tom Carpenter's conditional PROGMEM code +// http://forum.arduino.cc/index.php?topic=129407.0 +#ifdef __AVR__ + #include +#elif defined(ESP32) + #include +#else + // Teensy 3.0 library conditional PROGMEM code from Paul Stoffregen + #ifndef __PGMSPACE_H_ + #define __PGMSPACE_H_ 1 + #include + + #define PROGMEM + #define PGM_P const char * + #define PSTR(str) (str) + #define F(x) x + + typedef void prog_void; + typedef char prog_char; + typedef unsigned char prog_uchar; + typedef int8_t prog_int8_t; + typedef uint8_t prog_uint8_t; + typedef int16_t prog_int16_t; + typedef uint16_t prog_uint16_t; + typedef int32_t prog_int32_t; + typedef uint32_t prog_uint32_t; + + #define strcpy_P(dest, src) strcpy((dest), (src)) + #define strcat_P(dest, src) strcat((dest), (src)) + #define strcmp_P(a, b) strcmp((a), (b)) + + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + #define pgm_read_word(addr) (*(const unsigned short *)(addr)) + #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) + #define pgm_read_float(addr) (*(const float *)(addr)) + + #define pgm_read_byte_near(addr) pgm_read_byte(addr) + #define pgm_read_word_near(addr) pgm_read_word(addr) + #define pgm_read_dword_near(addr) pgm_read_dword(addr) + #define pgm_read_float_near(addr) pgm_read_float(addr) + #define pgm_read_byte_far(addr) pgm_read_byte(addr) + #define pgm_read_word_far(addr) pgm_read_word(addr) + #define pgm_read_dword_far(addr) pgm_read_dword(addr) + #define pgm_read_float_far(addr) pgm_read_float(addr) + #endif +#endif + +/* Source is from the InvenSense MotionApps v2 demo code. Original source is + * unavailable, unless you happen to be amazing as decompiling binary by + * hand (in which case, please contact me, and I'm totally serious). + * + * Also, I'd like to offer many, many thanks to Noah Zerkin for all of the + * DMP reverse-engineering he did to help make this bit of wizardry + * possible. + */ + +// NOTE! Enabling DEBUG adds about 3.3kB to the flash program size. +// Debug output is now working even on ATMega328P MCUs (e.g. Arduino Uno) +// after moving string constants to flash memory storage using the F() +// compiler macro (Arduino IDE 1.0+ required). + +//#define DEBUG +#ifdef DEBUG + #define DEBUG_PRINT(x) Serial.print(x) + #define DEBUG_PRINTF(x, y) Serial.print(x, y) + #define DEBUG_PRINTLN(x) Serial.println(x) + #define DEBUG_PRINTLNF(x, y) Serial.println(x, y) +#else + #define DEBUG_PRINT(x) + #define DEBUG_PRINTF(x, y) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTLNF(x, y) +#endif + +#define MPU6050_DMP_CODE_SIZE 1929 // dmpMemory[] +#define MPU6050_DMP_CONFIG_SIZE 192 // dmpConfig[] +#define MPU6050_DMP_UPDATES_SIZE 47 // dmpUpdates[] + +/* ================================================================================================ * + | Default MotionApps v2.0 42-byte FIFO packet structure: | + | | + | [QUAT W][ ][QUAT X][ ][QUAT Y][ ][QUAT Z][ ][GYRO X][ ][GYRO Y][ ] | + | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | + | | + | [GYRO Z][ ][ACC X ][ ][ACC Y ][ ][ACC Z ][ ][ ] | + | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | + * ================================================================================================ */ + +// this block of memory gets written to the MPU on start-up, and it seems +// to be volatile memory, so it has to be done each time (it only takes ~1 +// second though) + +// I Only Changed this by applying all the configuration data and capturing it before startup: +// *** this is a capture of the DMP Firmware after all the messy changes were made so we can just load it +static const unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] PROGMEM = { + /* bank # 0 */ + 0xFB, 0x00, 0x00, 0x3E, 0x00, 0x0B, 0x00, 0x36, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, 0xFA, 0x80, 0x00, 0x0B, 0x12, 0x82, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x00, 0x00, 0xFF, 0xFF, 0x45, 0x81, 0xFF, 0xFF, 0xFA, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7F, 0xFF, 0xFF, 0xFE, 0x80, 0x01, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0xCB, 0x47, 0xA2, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x41, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x2A, 0x00, 0x00, 0x16, 0x55, 0x00, 0x00, 0x21, 0x82, + 0xFD, 0x87, 0x26, 0x50, 0xFD, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x05, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x6F, 0x00, 0x02, 0x65, 0x32, 0x00, 0x00, 0x5E, 0xC0, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFB, 0x8C, 0x6F, 0x5D, 0xFD, 0x5D, 0x08, 0xD9, 0x00, 0x7C, 0x73, 0x3B, 0x00, 0x6C, 0x12, 0xCC, + 0x32, 0x00, 0x13, 0x9D, 0x32, 0x00, 0xD0, 0xD6, 0x32, 0x00, 0x08, 0x00, 0x40, 0x00, 0x01, 0xF4, + 0xFF, 0xE6, 0x80, 0x79, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xD6, 0x00, 0x00, 0x27, 0x10, + /* bank # 1 */ + 0xFB, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFA, 0x36, 0xFF, 0xBC, 0x30, 0x8E, 0x00, 0x05, 0xFB, 0xF0, 0xFF, 0xD9, 0x5B, 0xC8, + 0xFF, 0xD0, 0x9A, 0xBE, 0x00, 0x00, 0x10, 0xA9, 0xFF, 0xF4, 0x1E, 0xB2, 0x00, 0xCE, 0xBB, 0xF7, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x0C, + 0xFF, 0xC2, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xCF, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x23, 0xA1, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x3F, 0x68, 0xB6, 0x79, 0x35, 0x28, 0xBC, 0xC6, 0x7E, 0xD1, 0x6C, + 0x80, 0x00, 0xFF, 0xFF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x6A, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x25, 0x4D, 0x00, 0x2F, 0x70, 0x6D, 0x00, 0x00, 0x05, 0xAE, 0x00, 0x0C, 0x02, 0xD0, + /* bank # 2 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x44, 0x00, 0x01, 0x00, 0x05, 0x8B, 0xC1, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0xFF, 0xEF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* bank # 3 */ + 0xD8, 0xDC, 0xBA, 0xA2, 0xF1, 0xDE, 0xB2, 0xB8, 0xB4, 0xA8, 0x81, 0x91, 0xF7, 0x4A, 0x90, 0x7F, + 0x91, 0x6A, 0xF3, 0xF9, 0xDB, 0xA8, 0xF9, 0xB0, 0xBA, 0xA0, 0x80, 0xF2, 0xCE, 0x81, 0xF3, 0xC2, + 0xF1, 0xC1, 0xF2, 0xC3, 0xF3, 0xCC, 0xA2, 0xB2, 0x80, 0xF1, 0xC6, 0xD8, 0x80, 0xBA, 0xA7, 0xDF, + 0xDF, 0xDF, 0xF2, 0xA7, 0xC3, 0xCB, 0xC5, 0xB6, 0xF0, 0x87, 0xA2, 0x94, 0x24, 0x48, 0x70, 0x3C, + 0x95, 0x40, 0x68, 0x34, 0x58, 0x9B, 0x78, 0xA2, 0xF1, 0x83, 0x92, 0x2D, 0x55, 0x7D, 0xD8, 0xB1, + 0xB4, 0xB8, 0xA1, 0xD0, 0x91, 0x80, 0xF2, 0x70, 0xF3, 0x70, 0xF2, 0x7C, 0x80, 0xA8, 0xF1, 0x01, + 0xB0, 0x98, 0x87, 0xD9, 0x43, 0xD8, 0x86, 0xC9, 0x88, 0xBA, 0xA1, 0xF2, 0x0E, 0xB8, 0x97, 0x80, + 0xF1, 0xA9, 0xDF, 0xDF, 0xDF, 0xAA, 0xDF, 0xDF, 0xDF, 0xF2, 0xAA, 0x4C, 0xCD, 0x6C, 0xA9, 0x0C, + 0xC9, 0x2C, 0x97, 0x97, 0x97, 0x97, 0xF1, 0xA9, 0x89, 0x26, 0x46, 0x66, 0xB0, 0xB4, 0xBA, 0x80, + 0xAC, 0xDE, 0xF2, 0xCA, 0xF1, 0xB2, 0x8C, 0x02, 0xA9, 0xB6, 0x98, 0x00, 0x89, 0x0E, 0x16, 0x1E, + 0xB8, 0xA9, 0xB4, 0x99, 0x2C, 0x54, 0x7C, 0xB0, 0x8A, 0xA8, 0x96, 0x36, 0x56, 0x76, 0xF1, 0xB9, + 0xAF, 0xB4, 0xB0, 0x83, 0xC0, 0xB8, 0xA8, 0x97, 0x11, 0xB1, 0x8F, 0x98, 0xB9, 0xAF, 0xF0, 0x24, + 0x08, 0x44, 0x10, 0x64, 0x18, 0xF1, 0xA3, 0x29, 0x55, 0x7D, 0xAF, 0x83, 0xB5, 0x93, 0xAF, 0xF0, + 0x00, 0x28, 0x50, 0xF1, 0xA3, 0x86, 0x9F, 0x61, 0xA6, 0xDA, 0xDE, 0xDF, 0xD9, 0xFA, 0xA3, 0x86, + 0x96, 0xDB, 0x31, 0xA6, 0xD9, 0xF8, 0xDF, 0xBA, 0xA6, 0x8F, 0xC2, 0xC5, 0xC7, 0xB2, 0x8C, 0xC1, + 0xB8, 0xA2, 0xDF, 0xDF, 0xDF, 0xA3, 0xDF, 0xDF, 0xDF, 0xD8, 0xD8, 0xF1, 0xB8, 0xA8, 0xB2, 0x86, + /* bank # 4 */ + 0xB4, 0x98, 0x0D, 0x35, 0x5D, 0xB8, 0xAA, 0x98, 0xB0, 0x87, 0x2D, 0x35, 0x3D, 0xB2, 0xB6, 0xBA, + 0xAF, 0x8C, 0x96, 0x19, 0x8F, 0x9F, 0xA7, 0x0E, 0x16, 0x1E, 0xB4, 0x9A, 0xB8, 0xAA, 0x87, 0x2C, + 0x54, 0x7C, 0xB9, 0xA3, 0xDE, 0xDF, 0xDF, 0xA3, 0xB1, 0x80, 0xF2, 0xC4, 0xCD, 0xC9, 0xF1, 0xB8, + 0xA9, 0xB4, 0x99, 0x83, 0x0D, 0x35, 0x5D, 0x89, 0xB9, 0xA3, 0x2D, 0x55, 0x7D, 0xB5, 0x93, 0xA3, + 0x0E, 0x16, 0x1E, 0xA9, 0x2C, 0x54, 0x7C, 0xB8, 0xB4, 0xB0, 0xF1, 0x97, 0x83, 0xA8, 0x11, 0x84, + 0xA5, 0x09, 0x98, 0xA3, 0x83, 0xF0, 0xDA, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xD8, 0xF1, 0xA5, + 0x29, 0x55, 0x7D, 0xA5, 0x85, 0x95, 0x02, 0x1A, 0x2E, 0x3A, 0x56, 0x5A, 0x40, 0x48, 0xF9, 0xF3, + 0xA3, 0xD9, 0xF8, 0xF0, 0x98, 0x83, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0x97, 0x82, 0xA8, 0xF1, + 0x11, 0xF0, 0x98, 0xA2, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xDA, 0xF3, 0xDE, 0xD8, 0x83, 0xA5, + 0x94, 0x01, 0xD9, 0xA3, 0x02, 0xF1, 0xA2, 0xC3, 0xC5, 0xC7, 0xD8, 0xF1, 0x84, 0x92, 0xA2, 0x4D, + 0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32, 0xD8, 0x50, 0x71, 0xD9, + 0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8, 0x78, 0x93, 0xA3, 0x4D, + 0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32, 0xD8, 0x50, 0x71, 0xD9, + 0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8, 0x78, 0xA8, 0x8A, 0x9A, + 0xF0, 0x28, 0x50, 0x78, 0x9E, 0xF3, 0x88, 0x18, 0xF1, 0x9F, 0x1D, 0x98, 0xA8, 0xD9, 0x08, 0xD8, + 0xC8, 0x9F, 0x12, 0x9E, 0xF3, 0x15, 0xA8, 0xDA, 0x12, 0x10, 0xD8, 0xF1, 0xAF, 0xC8, 0x97, 0x87, + /* bank # 5 */ + 0x34, 0xB5, 0xB9, 0x94, 0xA4, 0x21, 0xF3, 0xD9, 0x22, 0xD8, 0xF2, 0x2D, 0xF3, 0xD9, 0x2A, 0xD8, + 0xF2, 0x35, 0xF3, 0xD9, 0x32, 0xD8, 0x81, 0xA4, 0x60, 0x60, 0x61, 0xD9, 0x61, 0xD8, 0x6C, 0x68, + 0x69, 0xD9, 0x69, 0xD8, 0x74, 0x70, 0x71, 0xD9, 0x71, 0xD8, 0xB1, 0xA3, 0x84, 0x19, 0x3D, 0x5D, + 0xA3, 0x83, 0x1A, 0x3E, 0x5E, 0x93, 0x10, 0x30, 0x81, 0x10, 0x11, 0xB8, 0xB0, 0xAF, 0x8F, 0x94, + 0xF2, 0xDA, 0x3E, 0xD8, 0xB4, 0x9A, 0xA8, 0x87, 0x29, 0xDA, 0xF8, 0xD8, 0x87, 0x9A, 0x35, 0xDA, + 0xF8, 0xD8, 0x87, 0x9A, 0x3D, 0xDA, 0xF8, 0xD8, 0xB1, 0xB9, 0xA4, 0x98, 0x85, 0x02, 0x2E, 0x56, + 0xA5, 0x81, 0x00, 0x0C, 0x14, 0xA3, 0x97, 0xB0, 0x8A, 0xF1, 0x2D, 0xD9, 0x28, 0xD8, 0x4D, 0xD9, + 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x84, 0x0D, 0xDA, 0x0E, 0xD8, 0xA3, 0x29, 0x83, 0xDA, + 0x2C, 0x0E, 0xD8, 0xA3, 0x84, 0x49, 0x83, 0xDA, 0x2C, 0x4C, 0x0E, 0xD8, 0xB8, 0xB0, 0xA8, 0x8A, + 0x9A, 0xF5, 0x20, 0xAA, 0xDA, 0xDF, 0xD8, 0xA8, 0x40, 0xAA, 0xD0, 0xDA, 0xDE, 0xD8, 0xA8, 0x60, + 0xAA, 0xDA, 0xD0, 0xDF, 0xD8, 0xF1, 0x97, 0x86, 0xA8, 0x31, 0x9B, 0x06, 0x99, 0x07, 0xAB, 0x97, + 0x28, 0x88, 0x9B, 0xF0, 0x0C, 0x20, 0x14, 0x40, 0xB8, 0xB0, 0xB4, 0xA8, 0x8C, 0x9C, 0xF0, 0x04, + 0x28, 0x51, 0x79, 0x1D, 0x30, 0x14, 0x38, 0xB2, 0x82, 0xAB, 0xD0, 0x98, 0x2C, 0x50, 0x50, 0x78, + 0x78, 0x9B, 0xF1, 0x1A, 0xB0, 0xF0, 0x8A, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0x8B, 0x29, 0x51, 0x79, + 0x8A, 0x24, 0x70, 0x59, 0x8B, 0x20, 0x58, 0x71, 0x8A, 0x44, 0x69, 0x38, 0x8B, 0x39, 0x40, 0x68, + 0x8A, 0x64, 0x48, 0x31, 0x8B, 0x30, 0x49, 0x60, 0xA5, 0x88, 0x20, 0x09, 0x71, 0x58, 0x44, 0x68, + /* bank # 6 */ + 0x11, 0x39, 0x64, 0x49, 0x30, 0x19, 0xF1, 0xAC, 0x00, 0x2C, 0x54, 0x7C, 0xF0, 0x8C, 0xA8, 0x04, + 0x28, 0x50, 0x78, 0xF1, 0x88, 0x97, 0x26, 0xA8, 0x59, 0x98, 0xAC, 0x8C, 0x02, 0x26, 0x46, 0x66, + 0xF0, 0x89, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69, 0x38, 0x64, 0x48, 0x31, + 0xA9, 0x88, 0x09, 0x20, 0x59, 0x70, 0xAB, 0x11, 0x38, 0x40, 0x69, 0xA8, 0x19, 0x31, 0x48, 0x60, + 0x8C, 0xA8, 0x3C, 0x41, 0x5C, 0x20, 0x7C, 0x00, 0xF1, 0x87, 0x98, 0x19, 0x86, 0xA8, 0x6E, 0x76, + 0x7E, 0xA9, 0x99, 0x88, 0x2D, 0x55, 0x7D, 0x9E, 0xB9, 0xA3, 0x8A, 0x22, 0x8A, 0x6E, 0x8A, 0x56, + 0x8A, 0x5E, 0x9F, 0xB1, 0x83, 0x06, 0x26, 0x46, 0x66, 0x0E, 0x2E, 0x4E, 0x6E, 0x9D, 0xB8, 0xAD, + 0x00, 0x2C, 0x54, 0x7C, 0xF2, 0xB1, 0x8C, 0xB4, 0x99, 0xB9, 0xA3, 0x2D, 0x55, 0x7D, 0x81, 0x91, + 0xAC, 0x38, 0xAD, 0x3A, 0xB5, 0x83, 0x91, 0xAC, 0x2D, 0xD9, 0x28, 0xD8, 0x4D, 0xD9, 0x48, 0xD8, + 0x6D, 0xD9, 0x68, 0xD8, 0x8C, 0x9D, 0xAE, 0x29, 0xD9, 0x04, 0xAE, 0xD8, 0x51, 0xD9, 0x04, 0xAE, + 0xD8, 0x79, 0xD9, 0x04, 0xD8, 0x81, 0xF3, 0x9D, 0xAD, 0x00, 0x8D, 0xAE, 0x19, 0x81, 0xAD, 0xD9, + 0x01, 0xD8, 0xF2, 0xAE, 0xDA, 0x26, 0xD8, 0x8E, 0x91, 0x29, 0x83, 0xA7, 0xD9, 0xAD, 0xAD, 0xAD, + 0xAD, 0xF3, 0x2A, 0xD8, 0xD8, 0xF1, 0xB0, 0xAC, 0x89, 0x91, 0x3E, 0x5E, 0x76, 0xF3, 0xAC, 0x2E, + 0x2E, 0xF1, 0xB1, 0x8C, 0x5A, 0x9C, 0xAC, 0x2C, 0x28, 0x28, 0x28, 0x9C, 0xAC, 0x30, 0x18, 0xA8, + 0x98, 0x81, 0x28, 0x34, 0x3C, 0x97, 0x24, 0xA7, 0x28, 0x34, 0x3C, 0x9C, 0x24, 0xF2, 0xB0, 0x89, + 0xAC, 0x91, 0x2C, 0x4C, 0x6C, 0x8A, 0x9B, 0x2D, 0xD9, 0xD8, 0xD8, 0x51, 0xD9, 0xD8, 0xD8, 0x79, + /* bank # 7 */ + 0xD9, 0xD8, 0xD8, 0xF1, 0x9E, 0x88, 0xA3, 0x31, 0xDA, 0xD8, 0xD8, 0x91, 0x2D, 0xD9, 0x28, 0xD8, + 0x4D, 0xD9, 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x83, 0x93, 0x35, 0x3D, 0x80, 0x25, 0xDA, + 0xD8, 0xD8, 0x85, 0x69, 0xDA, 0xD8, 0xD8, 0xB4, 0x93, 0x81, 0xA3, 0x28, 0x34, 0x3C, 0xF3, 0xAB, + 0x8B, 0xF8, 0xA3, 0x91, 0xB6, 0x09, 0xB4, 0xD9, 0xAB, 0xDE, 0xFA, 0xB0, 0x87, 0x9C, 0xB9, 0xA3, + 0xDD, 0xF1, 0x20, 0x28, 0x30, 0x38, 0x9A, 0xF1, 0x28, 0x30, 0x38, 0x9D, 0xF1, 0xA3, 0xA3, 0xA3, + 0xA3, 0xF2, 0xA3, 0xB4, 0x90, 0x80, 0xF2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xB2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xB0, 0x87, 0xB5, 0x99, 0xF1, 0x28, 0x30, 0x38, + 0x98, 0xF1, 0xA3, 0xA3, 0xA3, 0xA3, 0x97, 0xA3, 0xA3, 0xA3, 0xA3, 0xF3, 0x9B, 0xA3, 0x30, 0xDC, + 0xB9, 0xA7, 0xF1, 0x26, 0x26, 0x26, 0xFE, 0xD8, 0xFF, + +}; + +#ifndef MPU6050_DMP_FIFO_RATE_DIVISOR +#define MPU6050_DMP_FIFO_RATE_DIVISOR 0x01 // The New instance of the Firmware has this as the default +#endif + +// I Simplified this: +uint8_t MPU6050_6Axis_MotionApps20::dmpInitialize() { + // reset device + DEBUG_PRINTLN(F("\n\nResetting MPU6050...")); + reset(); + delay(30); // wait after reset + + // enable sleep mode and wake cycle + /*Serial.println(F("Enabling sleep mode...")); + setSleepEnabled(true); + Serial.println(F("Enabling wake cycle...")); + setWakeCycleEnabled(true);*/ + + // disable sleep mode + setSleepEnabled(false); + + // get MPU hardware revision + setMemoryBank(0x10, true, true); + setMemoryStartAddress(0x06); + DEBUG_PRINTLN(F("Checking hardware revision...")); + DEBUG_PRINT(F("Revision @ user[16][6] = ")); + DEBUG_PRINTLN(readMemoryByte()); + DEBUG_PRINTLN(F("Resetting memory bank selection to 0...")); + setMemoryBank(0, false, false); + + // check OTP bank valid + DEBUG_PRINTLN(F("Reading OTP bank valid flag...")); + DEBUG_PRINT(F("OTP bank is ")); + DEBUG_PRINTLN(getOTPBankValid() ? F("valid!") : F("invalid!")); + + // setup weird slave stuff (?) + DEBUG_PRINTLN(F("Setting slave 0 address to 0x7F...")); + setSlaveAddress(0, 0x7F); + DEBUG_PRINTLN(F("Disabling I2C Master mode...")); + setI2CMasterModeEnabled(false); + DEBUG_PRINTLN(F("Setting slave 0 address to 0x68 (self)...")); + setSlaveAddress(0, 0x68); + DEBUG_PRINTLN(F("Resetting I2C Master control...")); + resetI2CMaster(); + delay(20); + DEBUG_PRINTLN(F("Setting clock source to Z Gyro...")); + setClockSource(MPU6050_CLOCK_PLL_ZGYRO); + + DEBUG_PRINTLN(F("Setting DMP and FIFO_OFLOW interrupts enabled...")); + setIntEnabled(1<= dmpGetFIFOPacketSize(); +} + +// uint8_t MPU6050_6Axis_MotionApps20::dmpSetFIFORate(uint8_t fifoRate); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetFIFORate(); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetSampleStepSizeMS(); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetSampleFrequency(); +// int32_t MPU6050_6Axis_MotionApps20::dmpDecodeTemperature(int8_t tempReg); + +//uint8_t MPU6050_6Axis_MotionApps20::dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); +//uint8_t MPU6050_6Axis_MotionApps20::dmpUnregisterFIFORateProcess(inv_obj_func func); +//uint8_t MPU6050_6Axis_MotionApps20::dmpRunFIFORateProcesses(); + +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendQuaternion(uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendPacketNumber(uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); + +uint8_t MPU6050_6Axis_MotionApps20::dmpGetAccel(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[28] << 24) | ((uint32_t)packet[29] << 16) | ((uint32_t)packet[30] << 8) | packet[31]); + data[1] = (((uint32_t)packet[32] << 24) | ((uint32_t)packet[33] << 16) | ((uint32_t)packet[34] << 8) | packet[35]); + data[2] = (((uint32_t)packet[36] << 24) | ((uint32_t)packet[37] << 16) | ((uint32_t)packet[38] << 8) | packet[39]); + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpGetAccel(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (packet[28] << 8) | packet[29]; + data[1] = (packet[32] << 8) | packet[33]; + data[2] = (packet[36] << 8) | packet[37]; + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpGetAccel(VectorInt16 *v, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + v -> x = (packet[28] << 8) | packet[29]; + v -> y = (packet[32] << 8) | packet[33]; + v -> z = (packet[36] << 8) | packet[37]; + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpGetQuaternion(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[0] << 24) | ((uint32_t)packet[1] << 16) | ((uint32_t)packet[2] << 8) | packet[3]); + data[1] = (((uint32_t)packet[4] << 24) | ((uint32_t)packet[5] << 16) | ((uint32_t)packet[6] << 8) | packet[7]); + data[2] = (((uint32_t)packet[8] << 24) | ((uint32_t)packet[9] << 16) | ((uint32_t)packet[10] << 8) | packet[11]); + data[3] = (((uint32_t)packet[12] << 24) | ((uint32_t)packet[13] << 16) | ((uint32_t)packet[14] << 8) | packet[15]); + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpGetQuaternion(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = ((packet[0] << 8) | packet[1]); + data[1] = ((packet[4] << 8) | packet[5]); + data[2] = ((packet[8] << 8) | packet[9]); + data[3] = ((packet[12] << 8) | packet[13]); + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpGetQuaternion(Quaternion *q, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + int16_t qI[4]; + uint8_t status = dmpGetQuaternion(qI, packet); + if (status == 0) { + q -> w = (float)qI[0] / 16384.0f; + q -> x = (float)qI[1] / 16384.0f; + q -> y = (float)qI[2] / 16384.0f; + q -> z = (float)qI[3] / 16384.0f; + return 0; + } + return status; // int16 return value, indicates error if this line is reached +} +// uint8_t MPU6050_6Axis_MotionApps20::dmpGet6AxisQuaternion(long *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetRelativeQuaternion(long *data, const uint8_t* packet); +uint8_t MPU6050_6Axis_MotionApps20::dmpGetGyro(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[16] << 24) | ((uint32_t)packet[17] << 16) | ((uint32_t)packet[18] << 8) | packet[19]); + data[1] = (((uint32_t)packet[20] << 24) | ((uint32_t)packet[21] << 16) | ((uint32_t)packet[22] << 8) | packet[23]); + data[2] = (((uint32_t)packet[24] << 24) | ((uint32_t)packet[25] << 16) | ((uint32_t)packet[26] << 8) | packet[27]); + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpGetGyro(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (packet[16] << 8) | packet[17]; + data[1] = (packet[20] << 8) | packet[21]; + data[2] = (packet[24] << 8) | packet[25]; + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpGetGyro(VectorInt16 *v, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + v -> x = (packet[16] << 8) | packet[17]; + v -> y = (packet[20] << 8) | packet[21]; + v -> z = (packet[24] << 8) | packet[25]; + return 0; +} +// uint8_t MPU6050_6Axis_MotionApps20::dmpSetLinearAccelFilterCoefficient(float coef); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetLinearAccel(long *data, const uint8_t* packet); +uint8_t MPU6050_6Axis_MotionApps20::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) { + // get rid of the gravity component (+1g = +8192 in standard DMP FIFO packet, sensitivity is 2g) + v -> x = vRaw -> x - gravity -> x*8192; + v -> y = vRaw -> y - gravity -> y*8192; + v -> z = vRaw -> z - gravity -> z*8192; + return 0; +} +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetLinearAccelInWorld(long *data, const uint8_t* packet); +uint8_t MPU6050_6Axis_MotionApps20::dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q) { + // rotate measured 3D acceleration vector into original state + // frame of reference based on orientation quaternion + memcpy(v, vReal, sizeof(VectorInt16)); + v -> rotate(q); + return 0; +} +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetGyroAndAccelSensor(long *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetGyroSensor(long *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetControlData(long *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetTemperature(long *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetGravity(long *data, const uint8_t* packet); +uint8_t MPU6050_6Axis_MotionApps20::dmpGetGravity(int16_t *data, const uint8_t* packet) { + /* +1g corresponds to +8192, sensitivity is 2g. */ + int16_t qI[4]; + uint8_t status = dmpGetQuaternion(qI, packet); + data[0] = ((int32_t)qI[1] * qI[3] - (int32_t)qI[0] * qI[2]) / 16384; + data[1] = ((int32_t)qI[0] * qI[1] + (int32_t)qI[2] * qI[3]) / 16384; + data[2] = ((int32_t)qI[0] * qI[0] - (int32_t)qI[1] * qI[1] + - (int32_t)qI[2] * qI[2] + (int32_t)qI[3] * qI[3]) / (int32_t)(2 * 16384L); + return status; +} + +uint8_t MPU6050_6Axis_MotionApps20::dmpGetGravity(VectorFloat *v, Quaternion *q) { + v -> x = 2 * (q -> x*q -> z - q -> w*q -> y); + v -> y = 2 * (q -> w*q -> x + q -> y*q -> z); + v -> z = q -> w*q -> w - q -> x*q -> x - q -> y*q -> y + q -> z*q -> z; + return 0; +} +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetUnquantizedAccel(long *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetQuantizedAccel(long *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetExternalSensorData(long *data, int size, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetEIS(long *data, const uint8_t* packet); + +uint8_t MPU6050_6Axis_MotionApps20::dmpGetEuler(float *data, Quaternion *q) { + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); // psi + data[1] = -asin(2*q -> x*q -> z + 2*q -> w*q -> y); // theta + data[2] = atan2(2*q -> y*q -> z - 2*q -> w*q -> x, 2*q -> w*q -> w + 2*q -> z*q -> z - 1); // phi + return 0; +} + +#ifdef USE_OLD_DMPGETYAWPITCHROLL +uint8_t MPU6050_6Axis_MotionApps20::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) { + // yaw: (about Z axis) + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); + // pitch: (nose up/down, about Y axis) + data[1] = atan(gravity -> x / sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z)); + // roll: (tilt left/right, about X axis) + data[2] = atan(gravity -> y / sqrt(gravity -> x*gravity -> x + gravity -> z*gravity -> z)); + return 0; +} +#else +uint8_t MPU6050_6Axis_MotionApps20::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) { + // yaw: (about Z axis) + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); + // pitch: (nose up/down, about Y axis) + data[1] = atan2(gravity -> x , sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z)); + // roll: (tilt left/right, about X axis) + data[2] = atan2(gravity -> y , gravity -> z); + if (gravity -> z < 0) { + if(data[1] > 0) { + data[1] = PI - data[1]; + } else { + data[1] = -PI - data[1]; + } + } + return 0; +} +#endif + +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetAccelFloat(float *data, const uint8_t* packet); +// uint8_t MPU6050_6Axis_MotionApps20::dmpGetQuaternionFloat(float *data, const uint8_t* packet); + +uint8_t MPU6050_6Axis_MotionApps20::dmpProcessFIFOPacket(const unsigned char *dmpData) { + (void)dmpData; // unused parameter + /*for (uint8_t k = 0; k < dmpPacketSize; k++) { + if (dmpData[k] < 0x10) Serial.print("0"); + Serial.print(dmpData[k], HEX); + Serial.print(" "); + } + Serial.print("\n");*/ + //Serial.println((uint16_t)dmpPacketBuffer); + return 0; +} +uint8_t MPU6050_6Axis_MotionApps20::dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed) { + uint8_t status; + uint8_t buf[dmpPacketSize]; + for (uint8_t i = 0; i < numPackets; i++) { + // read packet from FIFO + getFIFOBytes(buf, dmpPacketSize); + + // process packet + if ((status = dmpProcessFIFOPacket(buf)) > 0) return status; + + // increment external process count variable, if supplied + if (processed != 0) (*processed)++; + } + return 0; +} + +// uint8_t MPU6050_6Axis_MotionApps20::dmpSetFIFOProcessedCallback(void (*func) (void)); + +// uint8_t MPU6050_6Axis_MotionApps20::dmpInitFIFOParam(); +// uint8_t MPU6050_6Axis_MotionApps20::dmpCloseFIFO(); +// uint8_t MPU6050_6Axis_MotionApps20::dmpSetGyroDataSource(uint_fast8_t source); +// uint8_t MPU6050_6Axis_MotionApps20::dmpDecodeQuantizedAccel(); +// uint32_t MPU6050_6Axis_MotionApps20::dmpGetGyroSumOfSquare(); +// uint32_t MPU6050_6Axis_MotionApps20::dmpGetAccelSumOfSquare(); +// void MPU6050_6Axis_MotionApps20::dmpOverrideQuaternion(long *q); +uint16_t MPU6050_6Axis_MotionApps20::dmpGetFIFOPacketSize() { + return dmpPacketSize; +} + + + +uint8_t MPU6050_6Axis_MotionApps20::dmpGetCurrentFIFOPacket(uint8_t *data) { // overflow proof + return(GetCurrentFIFOPacket(data, dmpPacketSize)); +} diff --git a/MPU6050_6Axis_MotionApps20.h b/MPU6050_6Axis_MotionApps20.h new file mode 100644 index 0000000..0dc9379 --- /dev/null +++ b/MPU6050_6Axis_MotionApps20.h @@ -0,0 +1,153 @@ +// I2Cdev library collection - MPU6050 I2C device class +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 10/3/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021/09/27 - split implementations out of header files, finally +// ... - ongoing debug release + +// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE +// DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF +// YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING. + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#ifndef _MPU6050_6AXIS_MOTIONAPPS20_H_ +#define _MPU6050_6AXIS_MOTIONAPPS20_H_ + +// take ownership of the "MPU6050" typedef +#define I2CDEVLIB_MPU6050_TYPEDEF + +#include "MPU6050.h" + +class MPU6050_6Axis_MotionApps20 : public MPU6050_Base { + public: + MPU6050_6Axis_MotionApps20(uint8_t address=MPU6050_DEFAULT_ADDRESS, void *wireObj=0) : MPU6050_Base(address, wireObj) { } + + uint8_t dmpInitialize(); + bool dmpPacketAvailable(); + + uint8_t dmpSetFIFORate(uint8_t fifoRate); + uint8_t dmpGetFIFORate(); + uint8_t dmpGetSampleStepSizeMS(); + uint8_t dmpGetSampleFrequency(); + int32_t dmpDecodeTemperature(int8_t tempReg); + + // Register callbacks after a packet of FIFO data is processed + //uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); + //uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func); + uint8_t dmpRunFIFORateProcesses(); + + // Setup FIFO for various output + uint8_t dmpSendQuaternion(uint_fast16_t accuracy); + uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendPacketNumber(uint_fast16_t accuracy); + uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); + + // Get Fixed Point data from FIFO + uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpSetLinearAccelFilterCoefficient(float coef); + uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity); + uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q); + uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q); + uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0); + uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0); + + uint8_t dmpGetEuler(float *data, Quaternion *q); + uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity); + + // Get Floating Point data from FIFO + uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0); + + uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData); + uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL); + + uint8_t dmpSetFIFOProcessedCallback(void (*func) (void)); + + uint8_t dmpInitFIFOParam(); + uint8_t dmpCloseFIFO(); + uint8_t dmpSetGyroDataSource(uint8_t source); + uint8_t dmpDecodeQuantizedAccel(); + uint32_t dmpGetGyroSumOfSquare(); + uint32_t dmpGetAccelSumOfSquare(); + void dmpOverrideQuaternion(long *q); + uint16_t dmpGetFIFOPacketSize(); + uint8_t dmpGetCurrentFIFOPacket(uint8_t *data); // overflow proof + + private: + uint8_t *dmpPacketBuffer; + uint16_t dmpPacketSize; +}; + +typedef MPU6050_6Axis_MotionApps20 MPU6050; + +#endif /* _MPU6050_6AXIS_MOTIONAPPS20_H_ */ diff --git a/MPU6050_6Axis_MotionApps612.cpp b/MPU6050_6Axis_MotionApps612.cpp new file mode 100644 index 0000000..2cd1113 --- /dev/null +++ b/MPU6050_6Axis_MotionApps612.cpp @@ -0,0 +1,617 @@ +// I2Cdev library collection - MPU6050 I2C device class, 6-axis MotionApps 6.12 implementation +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 5/20/2013 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021/09/27 - split implementations out of header files, finally +// 2019/07/10 - I incorporated DMP Firmware Version 6.12 Latest as of today with many features and bug fixes. +// - MPU6050 Registers have not changed just the DMP Image so that full backwards compatibility is present +// - Run-time calibration routine is enabled which calibrates after no motion state is detected +// - once no motion state is detected Calibration completes within 0.5 seconds +// - The Drawback is that the firmware image is larger. +// ... - ongoing debug release + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +// MotionApps 2.0 DMP implementation, built using the MPU-6050EVB evaluation board +#define MPU6050_INCLUDE_DMP_MOTIONAPPS612 + +#include "MPU6050_6Axis_MotionApps612.h" + +// Tom Carpenter's conditional PROGMEM code +// http://forum.arduino.cc/index.php?topic=129407.0 +#ifdef __AVR__ + #include +#elif defined(ESP32) + #include +#else + // Teensy 3.0 library conditional PROGMEM code from Paul Stoffregen + #ifndef __PGMSPACE_H_ + #define __PGMSPACE_H_ 1 + #include + + #define PROGMEM + #define PGM_P const char * + #define PSTR(str) (str) + #define F(x) x + + typedef void prog_void; + typedef char prog_char; + typedef unsigned char prog_uchar; + typedef int8_t prog_int8_t; + typedef uint8_t prog_uint8_t; + typedef int16_t prog_int16_t; + typedef uint16_t prog_uint16_t; + typedef int32_t prog_int32_t; + typedef uint32_t prog_uint32_t; + + #define strcpy_P(dest, src) strcpy((dest), (src)) + #define strcat_P(dest, src) strcat((dest), (src)) + #define strcmp_P(a, b) strcmp((a), (b)) + + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + #define pgm_read_word(addr) (*(const unsigned short *)(addr)) + #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) + #define pgm_read_float(addr) (*(const float *)(addr)) + + #define pgm_read_byte_near(addr) pgm_read_byte(addr) + #define pgm_read_word_near(addr) pgm_read_word(addr) + #define pgm_read_dword_near(addr) pgm_read_dword(addr) + #define pgm_read_float_near(addr) pgm_read_float(addr) + #define pgm_read_byte_far(addr) pgm_read_byte(addr) + #define pgm_read_word_far(addr) pgm_read_word(addr) + #define pgm_read_dword_far(addr) pgm_read_dword(addr) + #define pgm_read_float_far(addr) pgm_read_float(addr) + #endif +#endif + +/* Source is from the InvenSense MotionApps v2 demo code. Original source is + * unavailable, unless you happen to be amazing as decompiling binary by + * hand (in which case, please contact me, and I'm totally serious). + * + * Also, I'd like to offer many, many thanks to Noah Zerkin for all of the + * DMP reverse-engineering he did to help make this bit of wizardry + * possible. + */ + +// NOTE! Enabling DEBUG adds about 3.3kB to the flash program size. +// Debug output is now working even on ATMega328P MCUs (e.g. Arduino Uno) +// after moving string constants to flash memory storage using the F() +// compiler macro (Arduino IDE 1.0+ required). + +//#define DEBUG +#ifdef DEBUG + #define DEBUG_PRINT(x) Serial.print(x) + #define DEBUG_PRINTF(x, y) Serial.print(x, y) + #define DEBUG_PRINTLN(x) Serial.println(x) + #define DEBUG_PRINTLNF(x, y) Serial.println(x, y) +#else + #define DEBUG_PRINT(x) + #define DEBUG_PRINTF(x, y) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTLNF(x, y) +#endif + +#define MPU6050_DMP_CODE_SIZE 3062 // dmpMemory[] + +/* ================================================================ * + | Default MotionApps v6.12 28-byte FIFO packet structure: | + | | + | [QUAT W][ ][QUAT X][ ][QUAT Y][ ][QUAT Z][ ] | + | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | + | | + | [GYRO X][GYRO Y][GYRO Z][ACC X ][ACC Y ][ACC Z ] | + | 16 17 18 19 20 21 22 23 24 25 26 27 | + * ================================================================ */ + +// this block of memory gets written to the MPU on start-up, and it seems +// to be volatile memory, so it has to be done each time (it only takes ~1 +// second though) + +// *** this is a capture of the DMP Firmware V6.1.2 after all the messy changes were made so we can just load it +const unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] PROGMEM = { +/* bank # 0 */ +0x00, 0xF8, 0xF6, 0x2A, 0x3F, 0x68, 0xF5, 0x7A, 0x00, 0x06, 0xFF, 0xFE, 0x00, 0x03, 0x00, 0x00, +0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, 0xFA, 0x80, 0x00, 0x0B, 0x12, 0x82, 0x00, 0x01, +0x03, 0x0C, 0x30, 0xC3, 0x0A, 0x74, 0x56, 0x2D, 0x0D, 0x62, 0xDB, 0xC7, 0x16, 0xF4, 0xBA, 0x02, +0x38, 0x83, 0xF8, 0x83, 0x30, 0x00, 0xF8, 0x83, 0x25, 0x8E, 0xF8, 0x83, 0x30, 0x00, 0xF8, 0x83, +0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0xBD, 0xD8, 0x11, 0x24, 0x00, 0x04, 0x00, 0x1A, 0x82, 0x79, 0xA1, +0x00, 0x36, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x38, 0x83, 0x6F, 0xA2, +0x00, 0x3E, 0x03, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xCA, 0xE3, 0x09, 0x3E, 0x80, 0x00, 0x00, +0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, +0x1F, 0xA4, 0xE8, 0xE4, 0xFF, 0xF5, 0xDC, 0xB9, 0x00, 0x5B, 0x79, 0xCF, 0x1F, 0x3F, 0x78, 0x76, +0x00, 0x86, 0x7C, 0x5A, 0x00, 0x86, 0x23, 0x47, 0xFA, 0xB9, 0x86, 0x31, 0x00, 0x74, 0x87, 0x8A, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x05, 0xFF, 0xFF, 0xE9, 0xA8, 0x00, 0x00, 0x21, 0x82, +0xFA, 0xB8, 0x4D, 0x46, 0xFF, 0xFA, 0xDF, 0x3D, 0xFF, 0xFF, 0xB2, 0xB3, 0x00, 0x00, 0x00, 0x00, +0x3F, 0xFF, 0xBA, 0x98, 0x00, 0x5D, 0xAC, 0x08, 0x00, 0x0A, 0x63, 0x78, 0x00, 0x01, 0x46, 0x21, +0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x42, 0xB5, 0x00, 0x06, 0x00, 0x64, 0x00, 0x64, 0x00, 0x06, +0x14, 0x06, 0x02, 0x9F, 0x0F, 0x47, 0x91, 0x32, 0xD9, 0x0E, 0x9F, 0xC9, 0x1D, 0xCF, 0x4C, 0x34, +0x3B, 0xB6, 0x7A, 0xE8, 0x00, 0x64, 0x00, 0x06, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, +/* bank # 1 */ +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x07, 0x00, 0x00, 0xFF, 0xF1, 0x00, 0x00, 0xFA, 0x46, 0x00, 0x00, 0xA2, 0xB8, 0x00, 0x00, +0x10, 0x00, 0x00, 0x00, 0x04, 0xD6, 0x00, 0x00, 0x04, 0xCC, 0x00, 0x00, 0x04, 0xCC, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x06, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x05, 0x00, 0x64, 0x00, 0x20, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, +0x00, 0x00, 0x00, 0x32, 0xF8, 0x98, 0x00, 0x00, 0xFF, 0x65, 0x00, 0x00, 0x83, 0x0F, 0x00, 0x00, +0x00, 0x06, 0x00, 0x00, 0xFF, 0xF1, 0x00, 0x00, 0xFA, 0x46, 0x00, 0x00, 0xA2, 0xB8, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, +0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0xB2, 0x6A, 0x00, 0x02, 0x00, 0x00, +0x00, 0x01, 0xFB, 0x83, 0x00, 0x7C, 0x00, 0x00, 0xFB, 0x15, 0xFC, 0x00, 0x1F, 0xB4, 0xFF, 0x83, +0x00, 0x00, 0x00, 0x01, 0x00, 0x65, 0x00, 0x07, 0x00, 0x64, 0x03, 0xE8, 0x00, 0x64, 0x00, 0x28, +0x00, 0x00, 0xFF, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x16, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x10, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x10, 0x00, +/* bank # 2 */ +0x00, 0x28, 0x00, 0x00, 0xFF, 0xFF, 0x45, 0x81, 0xFF, 0xFF, 0xFA, 0x72, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x01, 0x00, 0x05, 0xBA, 0xC6, 0x00, 0x47, 0x78, 0xA2, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x14, +0x00, 0x00, 0x23, 0xBB, 0x00, 0x2E, 0xA2, 0x5B, 0x00, 0x00, 0x05, 0x68, 0x00, 0x0B, 0xCF, 0x49, +0x00, 0x04, 0xFF, 0xFD, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x64, 0x00, 0x07, 0x00, 0x08, 0x00, 0x06, 0x00, 0x06, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x2E, 0xA2, 0x5B, 0x00, 0x00, 0x05, 0x68, 0x00, 0x0B, 0xCF, 0x49, 0x00, 0x00, 0x00, 0x00, +0x00, 0xF8, 0xF6, 0x2A, 0x3F, 0x68, 0xF5, 0x7A, 0x00, 0x04, 0xFF, 0xFD, 0x00, 0x02, 0x00, 0x00, +0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x0E, +0xFF, 0xFF, 0xFF, 0xCF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xFF, 0xFF, 0xFF, 0x9C, +0x00, 0x00, 0x43, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, +0xFF, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* bank # 3 */ +0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x24, 0x26, 0xD3, +0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x96, 0x00, 0x3C, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x9E, 0x65, 0x5D, +0x0C, 0x0A, 0x4E, 0x68, 0xCD, 0xCF, 0x77, 0x09, 0x50, 0x16, 0x67, 0x59, 0xC6, 0x19, 0xCE, 0x82, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x71, 0x1C, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xD7, 0x84, 0x00, 0x03, 0x00, 0x00, 0x00, +0x00, 0x11, 0xDC, 0x47, 0x03, 0x00, 0x00, 0x00, 0xC7, 0x93, 0x8F, 0x9D, 0x1E, 0x1B, 0x1C, 0x19, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xDF, 0xA4, 0x38, 0x1F, 0x9E, 0x65, 0x5D, +0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x71, 0x1C, 0x02, 0x03, 0x18, 0x85, 0x00, 0x00, 0x40, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3F, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xF4, 0xC9, 0xFF, 0xFF, 0xBC, 0xF0, 0x00, 0x01, 0x0C, 0x0F, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xF5, 0xB7, 0xBA, 0xB3, 0x67, 0x7D, 0xDF, 0x7E, 0x72, 0x90, 0x2E, 0x55, 0x4C, 0xF6, 0xE6, 0x88, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* bank # 4 */ +0xD8, 0xDC, 0xB4, 0xB8, 0xB0, 0xD8, 0xB9, 0xAB, 0xF3, 0xF8, 0xFA, 0xB3, 0xB7, 0xBB, 0x8E, 0x9E, +0xAE, 0xF1, 0x32, 0xF5, 0x1B, 0xF1, 0xB4, 0xB8, 0xB0, 0x80, 0x97, 0xF1, 0xA9, 0xDF, 0xDF, 0xDF, +0xAA, 0xDF, 0xDF, 0xDF, 0xF2, 0xAA, 0x4C, 0xCD, 0x6C, 0xA9, 0x0C, 0xC9, 0x2C, 0x97, 0xF1, 0xA9, +0x89, 0x26, 0x46, 0x66, 0xB2, 0x89, 0x99, 0xA9, 0x2D, 0x55, 0x7D, 0xB0, 0xB0, 0x8A, 0xA8, 0x96, +0x36, 0x56, 0x76, 0xF1, 0xBA, 0xA3, 0xB4, 0xB2, 0x80, 0xC0, 0xB8, 0xA8, 0x97, 0x11, 0xB2, 0x83, +0x98, 0xBA, 0xA3, 0xF0, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xB2, 0xB9, 0xB4, 0x98, 0x83, 0xF1, +0xA3, 0x29, 0x55, 0x7D, 0xBA, 0xB5, 0xB1, 0xA3, 0x83, 0x93, 0xF0, 0x00, 0x28, 0x50, 0xF5, 0xB2, +0xB6, 0xAA, 0x83, 0x93, 0x28, 0x54, 0x7C, 0xF1, 0xB9, 0xA3, 0x82, 0x93, 0x61, 0xBA, 0xA2, 0xDA, +0xDE, 0xDF, 0xDB, 0x81, 0x9A, 0xB9, 0xAE, 0xF5, 0x60, 0x68, 0x70, 0xF1, 0xDA, 0xBA, 0xA2, 0xDF, +0xD9, 0xBA, 0xA2, 0xFA, 0xB9, 0xA3, 0x82, 0x92, 0xDB, 0x31, 0xBA, 0xA2, 0xD9, 0xBA, 0xA2, 0xF8, +0xDF, 0x85, 0xA4, 0xD0, 0xC1, 0xBB, 0xAD, 0x83, 0xC2, 0xC5, 0xC7, 0xB8, 0xA2, 0xDF, 0xDF, 0xDF, +0xBA, 0xA0, 0xDF, 0xDF, 0xDF, 0xD8, 0xD8, 0xF1, 0xB8, 0xAA, 0xB3, 0x8D, 0xB4, 0x98, 0x0D, 0x35, +0x5D, 0xB2, 0xB6, 0xBA, 0xAF, 0x8C, 0x96, 0x19, 0x8F, 0x9F, 0xA7, 0x0E, 0x16, 0x1E, 0xB4, 0x9A, +0xB8, 0xAA, 0x87, 0x2C, 0x54, 0x7C, 0xBA, 0xA4, 0xB0, 0x8A, 0xB6, 0x91, 0x32, 0x56, 0x76, 0xB2, +0x84, 0x94, 0xA4, 0xC8, 0x08, 0xCD, 0xD8, 0xB8, 0xB4, 0xB0, 0xF1, 0x99, 0x82, 0xA8, 0x2D, 0x55, +0x7D, 0x98, 0xA8, 0x0E, 0x16, 0x1E, 0xA2, 0x2C, 0x54, 0x7C, 0x92, 0xA4, 0xF0, 0x2C, 0x50, 0x78, +/* bank # 5 */ +0xF1, 0x84, 0xA8, 0x98, 0xC4, 0xCD, 0xFC, 0xD8, 0x0D, 0xDB, 0xA8, 0xFC, 0x2D, 0xF3, 0xD9, 0xBA, +0xA6, 0xF8, 0xDA, 0xBA, 0xA6, 0xDE, 0xD8, 0xBA, 0xB2, 0xB6, 0x86, 0x96, 0xA6, 0xD0, 0xF3, 0xC8, +0x41, 0xDA, 0xA6, 0xC8, 0xF8, 0xD8, 0xB0, 0xB4, 0xB8, 0x82, 0xA8, 0x92, 0xF5, 0x2C, 0x54, 0x88, +0x98, 0xF1, 0x35, 0xD9, 0xF4, 0x18, 0xD8, 0xF1, 0xA2, 0xD0, 0xF8, 0xF9, 0xA8, 0x84, 0xD9, 0xC7, +0xDF, 0xF8, 0xF8, 0x83, 0xC5, 0xDA, 0xDF, 0x69, 0xDF, 0x83, 0xC1, 0xD8, 0xF4, 0x01, 0x14, 0xF1, +0xA8, 0x82, 0x4E, 0xA8, 0x84, 0xF3, 0x11, 0xD1, 0x82, 0xF5, 0xD9, 0x92, 0x28, 0x97, 0x88, 0xF1, +0x09, 0xF4, 0x1C, 0x1C, 0xD8, 0x84, 0xA8, 0xF3, 0xC0, 0xF9, 0xD1, 0xD9, 0x97, 0x82, 0xF1, 0x29, +0xF4, 0x0D, 0xD8, 0xF3, 0xF9, 0xF9, 0xD1, 0xD9, 0x82, 0xF4, 0xC2, 0x03, 0xD8, 0xDE, 0xDF, 0x1A, +0xD8, 0xF1, 0xA2, 0xFA, 0xF9, 0xA8, 0x84, 0x98, 0xD9, 0xC7, 0xDF, 0xF8, 0xF8, 0xF8, 0x83, 0xC7, +0xDA, 0xDF, 0x69, 0xDF, 0xF8, 0x83, 0xC3, 0xD8, 0xF4, 0x01, 0x14, 0xF1, 0x98, 0xA8, 0x82, 0x2E, +0xA8, 0x84, 0xF3, 0x11, 0xD1, 0x82, 0xF5, 0xD9, 0x92, 0x50, 0x97, 0x88, 0xF1, 0x09, 0xF4, 0x1C, +0xD8, 0x84, 0xA8, 0xF3, 0xC0, 0xF8, 0xF9, 0xD1, 0xD9, 0x97, 0x82, 0xF1, 0x49, 0xF4, 0x0D, 0xD8, +0xF3, 0xF9, 0xF9, 0xD1, 0xD9, 0x82, 0xF4, 0xC4, 0x03, 0xD8, 0xDE, 0xDF, 0xD8, 0xF1, 0xAD, 0x88, +0x98, 0xCC, 0xA8, 0x09, 0xF9, 0xD9, 0x82, 0x92, 0xA8, 0xF5, 0x7C, 0xF1, 0x88, 0x3A, 0xCF, 0x94, +0x4A, 0x6E, 0x98, 0xDB, 0x69, 0x31, 0xDA, 0xAD, 0xF2, 0xDE, 0xF9, 0xD8, 0x87, 0x95, 0xA8, 0xF2, +0x21, 0xD1, 0xDA, 0xA5, 0xF9, 0xF4, 0x17, 0xD9, 0xF1, 0xAE, 0x8E, 0xD0, 0xC0, 0xC3, 0xAE, 0x82, +/* bank # 6 */ +0xC6, 0x84, 0xC3, 0xA8, 0x85, 0x95, 0xC8, 0xA5, 0x88, 0xF2, 0xC0, 0xF1, 0xF4, 0x01, 0x0E, 0xF1, +0x8E, 0x9E, 0xA8, 0xC6, 0x3E, 0x56, 0xF5, 0x54, 0xF1, 0x88, 0x72, 0xF4, 0x01, 0x15, 0xF1, 0x98, +0x45, 0x85, 0x6E, 0xF5, 0x8E, 0x9E, 0x04, 0x88, 0xF1, 0x42, 0x98, 0x5A, 0x8E, 0x9E, 0x06, 0x88, +0x69, 0xF4, 0x01, 0x1C, 0xF1, 0x98, 0x1E, 0x11, 0x08, 0xD0, 0xF5, 0x04, 0xF1, 0x1E, 0x97, 0x02, +0x02, 0x98, 0x36, 0x25, 0xDB, 0xF9, 0xD9, 0x85, 0xA5, 0xF3, 0xC1, 0xDA, 0x85, 0xA5, 0xF3, 0xDF, +0xD8, 0x85, 0x95, 0xA8, 0xF3, 0x09, 0xDA, 0xA5, 0xFA, 0xD8, 0x82, 0x92, 0xA8, 0xF5, 0x78, 0xF1, +0x88, 0x1A, 0x84, 0x9F, 0x26, 0x88, 0x98, 0x21, 0xDA, 0xF4, 0x1D, 0xF3, 0xD8, 0x87, 0x9F, 0x39, +0xD1, 0xAF, 0xD9, 0xDF, 0xDF, 0xFB, 0xF9, 0xF4, 0x0C, 0xF3, 0xD8, 0xFA, 0xD0, 0xF8, 0xDA, 0xF9, +0xF9, 0xD0, 0xDF, 0xD9, 0xF9, 0xD8, 0xF4, 0x0B, 0xD8, 0xF3, 0x87, 0x9F, 0x39, 0xD1, 0xAF, 0xD9, +0xDF, 0xDF, 0xF4, 0x1D, 0xF3, 0xD8, 0xFA, 0xFC, 0xA8, 0x69, 0xF9, 0xF9, 0xAF, 0xD0, 0xDA, 0xDE, +0xFA, 0xD9, 0xF8, 0x8F, 0x9F, 0xA8, 0xF1, 0xCC, 0xF3, 0x98, 0xDB, 0x45, 0xD9, 0xAF, 0xDF, 0xD0, +0xF8, 0xD8, 0xF1, 0x8F, 0x9F, 0xA8, 0xCA, 0xF3, 0x88, 0x09, 0xDA, 0xAF, 0x8F, 0xCB, 0xF8, 0xD8, +0xF2, 0xAD, 0x97, 0x8D, 0x0C, 0xD9, 0xA5, 0xDF, 0xF9, 0xBA, 0xA6, 0xF3, 0xFA, 0xF4, 0x12, 0xF2, +0xD8, 0x95, 0x0D, 0xD1, 0xD9, 0xBA, 0xA6, 0xF3, 0xFA, 0xDA, 0xA5, 0xF2, 0xC1, 0xBA, 0xA6, 0xF3, +0xDF, 0xD8, 0xF1, 0xBA, 0xB2, 0xB6, 0x86, 0x96, 0xA6, 0xD0, 0xCA, 0xF3, 0x49, 0xDA, 0xA6, 0xCB, +0xF8, 0xD8, 0xB0, 0xB4, 0xB8, 0xD8, 0xAD, 0x84, 0xF2, 0xC0, 0xDF, 0xF1, 0x8F, 0xCB, 0xC3, 0xA8, +/* bank # 7 */ +0xB2, 0xB6, 0x86, 0x96, 0xC8, 0xC1, 0xCB, 0xC3, 0xF3, 0xB0, 0xB4, 0x88, 0x98, 0xA8, 0x21, 0xDB, +0x71, 0x8D, 0x9D, 0x71, 0x85, 0x95, 0x21, 0xD9, 0xAD, 0xF2, 0xFA, 0xD8, 0x85, 0x97, 0xA8, 0x28, +0xD9, 0xF4, 0x08, 0xD8, 0xF2, 0x8D, 0x29, 0xDA, 0xF4, 0x05, 0xD9, 0xF2, 0x85, 0xA4, 0xC2, 0xF2, +0xD8, 0xA8, 0x8D, 0x94, 0x01, 0xD1, 0xD9, 0xF4, 0x11, 0xF2, 0xD8, 0x87, 0x21, 0xD8, 0xF4, 0x0A, +0xD8, 0xF2, 0x84, 0x98, 0xA8, 0xC8, 0x01, 0xD1, 0xD9, 0xF4, 0x11, 0xD8, 0xF3, 0xA4, 0xC8, 0xBB, +0xAF, 0xD0, 0xF2, 0xDE, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xD8, 0xF1, 0xB8, 0xF6, +0xB5, 0xB9, 0xB0, 0x8A, 0x95, 0xA3, 0xDE, 0x3C, 0xA3, 0xD9, 0xF8, 0xD8, 0x5C, 0xA3, 0xD9, 0xF8, +0xD8, 0x7C, 0xA3, 0xD9, 0xF8, 0xD8, 0xF8, 0xF9, 0xD1, 0xA5, 0xD9, 0xDF, 0xDA, 0xFA, 0xD8, 0xB1, +0x85, 0x30, 0xF7, 0xD9, 0xDE, 0xD8, 0xF8, 0x30, 0xAD, 0xDA, 0xDE, 0xD8, 0xF2, 0xB4, 0x8C, 0x99, +0xA3, 0x2D, 0x55, 0x7D, 0xA0, 0x83, 0xDF, 0xDF, 0xDF, 0xB5, 0x91, 0xA0, 0xF6, 0x29, 0xD9, 0xFB, +0xD8, 0xA0, 0xFC, 0x29, 0xD9, 0xFA, 0xD8, 0xA0, 0xD0, 0x51, 0xD9, 0xF8, 0xD8, 0xFC, 0x51, 0xD9, +0xF9, 0xD8, 0x79, 0xD9, 0xFB, 0xD8, 0xA0, 0xD0, 0xFC, 0x79, 0xD9, 0xFA, 0xD8, 0xA1, 0xF9, 0xF9, +0xF9, 0xF9, 0xF9, 0xA0, 0xDA, 0xDF, 0xDF, 0xDF, 0xD8, 0xA1, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xAC, +0xDE, 0xF8, 0xAD, 0xDE, 0x83, 0x93, 0xAC, 0x2C, 0x54, 0x7C, 0xF1, 0xA8, 0xDF, 0xDF, 0xDF, 0xF6, +0x9D, 0x2C, 0xDA, 0xA0, 0xDF, 0xD9, 0xFA, 0xDB, 0x2D, 0xF8, 0xD8, 0xA8, 0x50, 0xDA, 0xA0, 0xD0, +0xDE, 0xD9, 0xD0, 0xF8, 0xF8, 0xF8, 0xDB, 0x55, 0xF8, 0xD8, 0xA8, 0x78, 0xDA, 0xA0, 0xD0, 0xDF, +/* bank # 8 */ +0xD9, 0xD0, 0xFA, 0xF8, 0xF8, 0xF8, 0xF8, 0xDB, 0x7D, 0xF8, 0xD8, 0x9C, 0xA8, 0x8C, 0xF5, 0x30, +0xDB, 0x38, 0xD9, 0xD0, 0xDE, 0xDF, 0xA0, 0xD0, 0xDE, 0xDF, 0xD8, 0xA8, 0x48, 0xDB, 0x58, 0xD9, +0xDF, 0xD0, 0xDE, 0xA0, 0xDF, 0xD0, 0xDE, 0xD8, 0xA8, 0x68, 0xDB, 0x70, 0xD9, 0xDF, 0xDF, 0xA0, +0xDF, 0xDF, 0xD8, 0xF1, 0xA8, 0x88, 0x90, 0x2C, 0x54, 0x7C, 0x98, 0xA8, 0xD0, 0x5C, 0x38, 0xD1, +0xDA, 0xF2, 0xAE, 0x8C, 0xDF, 0xF9, 0xD8, 0xB0, 0x87, 0xA8, 0xC1, 0xC1, 0xB1, 0x88, 0xA8, 0xC6, +0xF9, 0xF9, 0xDA, 0x36, 0xD8, 0xA8, 0xF9, 0xDA, 0x36, 0xD8, 0xA8, 0xF9, 0xDA, 0x36, 0xD8, 0xA8, +0xF9, 0xDA, 0x36, 0xD8, 0xA8, 0xF9, 0xDA, 0x36, 0xD8, 0xF7, 0x8D, 0x9D, 0xAD, 0xF8, 0x18, 0xDA, +0xF2, 0xAE, 0xDF, 0xD8, 0xF7, 0xAD, 0xFA, 0x30, 0xD9, 0xA4, 0xDE, 0xF9, 0xD8, 0xF2, 0xAE, 0xDE, +0xFA, 0xF9, 0x83, 0xA7, 0xD9, 0xC3, 0xC5, 0xC7, 0xF1, 0x88, 0x9B, 0xA7, 0x7A, 0xAD, 0xF7, 0xDE, +0xDF, 0xA4, 0xF8, 0x84, 0x94, 0x08, 0xA7, 0x97, 0xF3, 0x00, 0xAE, 0xF2, 0x98, 0x19, 0xA4, 0x88, +0xC6, 0xA3, 0x94, 0x88, 0xF6, 0x32, 0xDF, 0xF2, 0x83, 0x93, 0xDB, 0x09, 0xD9, 0xF2, 0xAA, 0xDF, +0xD8, 0xD8, 0xAE, 0xF8, 0xF9, 0xD1, 0xDA, 0xF3, 0xA4, 0xDE, 0xA7, 0xF1, 0x88, 0x9B, 0x7A, 0xD8, +0xF3, 0x84, 0x94, 0xAE, 0x19, 0xF9, 0xDA, 0xAA, 0xF1, 0xDF, 0xD8, 0xA8, 0x81, 0xC0, 0xC3, 0xC5, +0xC7, 0xA3, 0x92, 0x83, 0xF6, 0x28, 0xAD, 0xDE, 0xD9, 0xF8, 0xD8, 0xA3, 0x50, 0xAD, 0xD9, 0xF8, +0xD8, 0xA3, 0x78, 0xAD, 0xD9, 0xF8, 0xD8, 0xF8, 0xF9, 0xD1, 0xA1, 0xDA, 0xDE, 0xC3, 0xC5, 0xC7, +0xD8, 0xA1, 0x81, 0x94, 0xF8, 0x18, 0xF2, 0xB0, 0x89, 0xAC, 0xC3, 0xC5, 0xC7, 0xF1, 0xD8, 0xB8, +/* bank # 9 */ +0xB4, 0xB0, 0x97, 0x86, 0xA8, 0x31, 0x9B, 0x06, 0x99, 0x07, 0xAB, 0x97, 0x28, 0x88, 0x9B, 0xF0, +0x0C, 0x20, 0x14, 0x40, 0xB0, 0xB4, 0xB8, 0xF0, 0xA8, 0x8A, 0x9A, 0x28, 0x50, 0x78, 0xB7, 0x9B, +0xA8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69, 0x38, 0x64, 0x48, 0x31, 0xF1, 0xBB, 0xAB, +0x88, 0x00, 0x2C, 0x54, 0x7C, 0xF0, 0xB3, 0x8B, 0xB8, 0xA8, 0x04, 0x28, 0x50, 0x78, 0xF1, 0xB0, +0x88, 0xB4, 0x97, 0x26, 0xA8, 0x59, 0x98, 0xBB, 0xAB, 0xB3, 0x8B, 0x02, 0x26, 0x46, 0x66, 0xB0, +0xB8, 0xF0, 0x8A, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0x8B, 0x29, 0x51, 0x79, 0x8A, 0x24, 0x70, 0x59, +0x8B, 0x20, 0x58, 0x71, 0x8A, 0x44, 0x69, 0x38, 0x8B, 0x39, 0x40, 0x68, 0x8A, 0x64, 0x48, 0x31, +0x8B, 0x30, 0x49, 0x60, 0x88, 0xF1, 0xAC, 0x00, 0x2C, 0x54, 0x7C, 0xF0, 0x8C, 0xA8, 0x04, 0x28, +0x50, 0x78, 0xF1, 0x88, 0x97, 0x26, 0xA8, 0x59, 0x98, 0xAC, 0x8C, 0x02, 0x26, 0x46, 0x66, 0xF0, +0x89, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69, 0x38, 0x64, 0x48, 0x31, 0xA9, +0x88, 0x09, 0x20, 0x59, 0x70, 0xAB, 0x11, 0x38, 0x40, 0x69, 0xA8, 0x19, 0x31, 0x48, 0x60, 0x8C, +0xA8, 0x3C, 0x41, 0x5C, 0x20, 0x7C, 0x00, 0xF1, 0x87, 0x98, 0x19, 0x86, 0xA8, 0x6E, 0x76, 0x7E, +0xA9, 0x99, 0x88, 0x2D, 0x55, 0x7D, 0xD8, 0xB1, 0xB5, 0xB9, 0xA3, 0xDF, 0xDF, 0xDF, 0xAE, 0xD0, +0xDF, 0xAA, 0xD0, 0xDE, 0xF2, 0xAB, 0xF8, 0xF9, 0xD9, 0xB0, 0x87, 0xC4, 0xAA, 0xF1, 0xDF, 0xDF, +0xBB, 0xAF, 0xDF, 0xDF, 0xB9, 0xD8, 0xB1, 0xF1, 0xA3, 0x97, 0x8E, 0x60, 0xDF, 0xB0, 0x84, 0xF2, +0xC8, 0xF8, 0xF9, 0xD9, 0xDE, 0xD8, 0x93, 0x85, 0xF1, 0x4A, 0xB1, 0x83, 0xA3, 0x08, 0xB5, 0x83, +/* bank # 10 */ +0x9A, 0x08, 0x10, 0xB7, 0x9F, 0x10, 0xD8, 0xF1, 0xB0, 0xBA, 0xAE, 0xB0, 0x8A, 0xC2, 0xB2, 0xB6, +0x8E, 0x9E, 0xF1, 0xFB, 0xD9, 0xF4, 0x1D, 0xD8, 0xF9, 0xD9, 0x0C, 0xF1, 0xD8, 0xF8, 0xF8, 0xAD, +0x61, 0xD9, 0xAE, 0xFB, 0xD8, 0xF4, 0x0C, 0xF1, 0xD8, 0xF8, 0xF8, 0xAD, 0x19, 0xD9, 0xAE, 0xFB, +0xDF, 0xD8, 0xF4, 0x16, 0xF1, 0xD8, 0xF8, 0xAD, 0x8D, 0x61, 0xD9, 0xF4, 0xF4, 0xAC, 0xF5, 0x9C, +0x9C, 0x8D, 0xDF, 0x2B, 0xBA, 0xB6, 0xAE, 0xFA, 0xF8, 0xF4, 0x0B, 0xD8, 0xF1, 0xAE, 0xD0, 0xF8, +0xAD, 0x51, 0xDA, 0xAE, 0xFA, 0xF8, 0xF1, 0xD8, 0xB9, 0xB1, 0xB6, 0xA3, 0x83, 0x9C, 0x08, 0xB9, +0xB1, 0x83, 0x9A, 0xB5, 0xAA, 0xC0, 0xFD, 0x30, 0x83, 0xB7, 0x9F, 0x10, 0xB5, 0x8B, 0x93, 0xF2, +0x02, 0x02, 0xD1, 0xAB, 0xDA, 0xDE, 0xD8, 0xF1, 0xB0, 0x80, 0xBA, 0xAB, 0xC0, 0xC3, 0xB2, 0x84, +0xC1, 0xC3, 0xD8, 0xB1, 0xB9, 0xF3, 0x8B, 0xA3, 0x91, 0xB6, 0x09, 0xB4, 0xD9, 0xAB, 0xDE, 0xB0, +0x87, 0x9C, 0xB9, 0xA3, 0xDD, 0xF1, 0xB3, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0xB0, 0x87, 0x20, 0x28, +0x30, 0x38, 0xB2, 0x8B, 0xB6, 0x9B, 0xF2, 0xA3, 0xC0, 0xC8, 0xC2, 0xC4, 0xCC, 0xC6, 0xA3, 0xA3, +0xA3, 0xF1, 0xB0, 0x87, 0xB5, 0x9A, 0xD8, 0xF3, 0x9B, 0xA3, 0xA3, 0xDC, 0xBA, 0xAC, 0xDF, 0xB9, //Reverted back as packet size changes causing isues... TODO:change 2742 from 0xD8 to 0x20 Including the DMP_FEATURE_TAP -- known issue in which if you do not enable DMP_FEATURE_TAP then the interrupts will be at 200Hz even if fifo rate +0xA3, 0xFE, 0xF2, 0xAB, 0xC4, 0xAA, 0xF1, 0xDF, 0xDF, 0xBB, 0xAF, 0xDF, 0xDF, 0xA3, 0xA3, 0xA3, +0xD8, 0xD8, 0xD8, 0xBB, 0xB3, 0xB7, 0xF1, 0xAA, 0xF9, 0xDA, 0xFF, 0xD9, 0x80, 0x9A, 0xAA, 0x28, +0xB4, 0x80, 0x98, 0xA7, 0x20, 0xB7, 0x97, 0x87, 0xA8, 0x66, 0x88, 0xF0, 0x79, 0x51, 0xF1, 0x90, +0x2C, 0x87, 0x0C, 0xA7, 0x81, 0x97, 0x62, 0x93, 0xF0, 0x71, 0x71, 0x60, 0x85, 0x94, 0x01, 0x29, +/* bank # 11 */ +0x51, 0x79, 0x90, 0xA5, 0xF1, 0x28, 0x4C, 0x6C, 0x87, 0x0C, 0x95, 0x18, 0x85, 0x78, 0xA3, 0x83, +0x90, 0x28, 0x4C, 0x6C, 0x88, 0x6C, 0xD8, 0xF3, 0xA2, 0x82, 0x00, 0xF2, 0x10, 0xA8, 0x92, 0x19, +0x80, 0xA2, 0xF2, 0xD9, 0x26, 0xD8, 0xF1, 0x88, 0xA8, 0x4D, 0xD9, 0x48, 0xD8, 0x96, 0xA8, 0x39, +0x80, 0xD9, 0x3C, 0xD8, 0x95, 0x80, 0xA8, 0x39, 0xA6, 0x86, 0x98, 0xD9, 0x2C, 0xDA, 0x87, 0xA7, +0x2C, 0xD8, 0xA8, 0x89, 0x95, 0x19, 0xA9, 0x80, 0xD9, 0x38, 0xD8, 0xA8, 0x89, 0x39, 0xA9, 0x80, +0xDA, 0x3C, 0xD8, 0xA8, 0x2E, 0xA8, 0x39, 0x90, 0xD9, 0x0C, 0xD8, 0xA8, 0x95, 0x31, 0x98, 0xD9, +0x0C, 0xD8, 0xA8, 0x09, 0xD9, 0xFF, 0xD8, 0x01, 0xDA, 0xFF, 0xD8, 0x95, 0x39, 0xA9, 0xDA, 0x26, +0xFF, 0xD8, 0x90, 0xA8, 0x0D, 0x89, 0x99, 0xA8, 0x10, 0x80, 0x98, 0x21, 0xDA, 0x2E, 0xD8, 0x89, +0x99, 0xA8, 0x31, 0x80, 0xDA, 0x2E, 0xD8, 0xA8, 0x86, 0x96, 0x31, 0x80, 0xDA, 0x2E, 0xD8, 0xA8, +0x87, 0x31, 0x80, 0xDA, 0x2E, 0xD8, 0xA8, 0x82, 0x92, 0xF3, 0x41, 0x80, 0xF1, 0xD9, 0x2E, 0xD8, +0xA8, 0x82, 0xF3, 0x19, 0x80, 0xF1, 0xD9, 0x2E, 0xD8, 0x82, 0xAC, 0xF3, 0xC0, 0xA2, 0x80, 0x22, +0xF1, 0xA6, 0x2E, 0xA7, 0x2E, 0xA9, 0x22, 0x98, 0xA8, 0x29, 0xDA, 0xAC, 0xDE, 0xFF, 0xD8, 0xA2, +0xF2, 0x2A, 0xF1, 0xA9, 0x2E, 0x82, 0x92, 0xA8, 0xF2, 0x31, 0x80, 0xA6, 0x96, 0xF1, 0xD9, 0x00, +0xAC, 0x8C, 0x9C, 0x0C, 0x30, 0xAC, 0xDE, 0xD0, 0xDE, 0xFF, 0xD8, 0x8C, 0x9C, 0xAC, 0xD0, 0x10, +0xAC, 0xDE, 0x80, 0x92, 0xA2, 0xF2, 0x4C, 0x82, 0xA8, 0xF1, 0xCA, 0xF2, 0x35, 0xF1, 0x96, 0x88, +0xA6, 0xD9, 0x00, 0xD8, 0xF1, 0xFF, +}; + +// this divisor is pre configured into the above image and can't be modified at this time. +#ifndef MPU6050_DMP_FIFO_RATE_DIVISOR +#define MPU6050_DMP_FIFO_RATE_DIVISOR 0x01 // The New instance of the Firmware has this as the default +#endif + +// this is the most basic initialization I can create. with the intent that we access the register bytes as few times as needed to get the job done. +// for detailed descriptins of all registers and there purpose google "MPU-6000/MPU-6050 Register Map and Descriptions" +uint8_t MPU6050::dmpInitialize() { // Lets get it over with fast Write everything once and set it up necely + uint8_t val; + uint16_t ival; + // Reset procedure per instructions in the "MPU-6000/MPU-6050 Register Map and Descriptions" page 41 + I2Cdev::writeBit(devAddr,0x6B, 7, (val = 1), wireObj); //PWR_MGMT_1: reset with 100ms delay + delay(100); + I2Cdev::writeBits(devAddr,0x6A, 2, 3, (val = 0b111), wireObj); // full SIGNAL_PATH_RESET: with another 100ms delay + delay(100); + I2Cdev::writeBytes(devAddr,0x6B, 1, &(val = 0x01), wireObj); // 1000 0001 PWR_MGMT_1:Clock Source Select PLL_X_gyro + I2Cdev::writeBytes(devAddr,0x38, 1, &(val = 0x00), wireObj); // 0000 0000 INT_ENABLE: no Interrupt + I2Cdev::writeBytes(devAddr,0x23, 1, &(val = 0x00), wireObj); // 0000 0000 MPU FIFO_EN: (all off) Using DMP's FIFO instead + I2Cdev::writeBytes(devAddr,0x1C, 1, &(val = 0x00), wireObj); // 0000 0000 ACCEL_CONFIG: 0 = Accel Full Scale Select: 2g + I2Cdev::writeBytes(devAddr,0x37, 1, &(val = 0x80), wireObj); // 1001 0000 INT_PIN_CFG: ACTL The logic level for int pin is active low. and interrupt status bits are cleared on any read + I2Cdev::writeBytes(devAddr,0x6B, 1, &(val = 0x01), wireObj); // 0000 0001 PWR_MGMT_1: Clock Source Select PLL_X_gyro + I2Cdev::writeBytes(devAddr,0x19, 1, &(val = 0x04), wireObj); // 0000 0100 SMPLRT_DIV: Divides the internal sample rate 400Hz ( Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)) + I2Cdev::writeBytes(devAddr,0x1A, 1, &(val = 0x01), wireObj); // 0000 0001 CONFIG: Digital Low Pass Filter (DLPF) Configuration 188HZ //Im betting this will be the beat + if (!writeProgMemoryBlock(dmpMemory, MPU6050_DMP_CODE_SIZE)) return 1; // Loads the DMP image into the MPU6050 Memory // Should Never Fail + I2Cdev::writeWords(devAddr, 0x70, 1, &(ival = 0x0400), wireObj); // DMP Program Start Address + I2Cdev::writeBytes(devAddr,0x1B, 1, &(val = 0x18), wireObj); // 0001 1000 GYRO_CONFIG: 3 = +2000 Deg/sec + I2Cdev::writeBytes(devAddr,0x6A, 1, &(val = 0xC0), wireObj); // 1100 1100 USER_CTRL: Enable Fifo and Reset Fifo + I2Cdev::writeBytes(devAddr,0x38, 1, &(val = 0x02), wireObj); // 0000 0010 INT_ENABLE: RAW_DMP_INT_EN on + I2Cdev::writeBit(devAddr,0x6A, 2, 1, wireObj); // Reset FIFO one last time just for kicks. (MPUi2cWrite reads 0x6A first and only alters 1 bit and then saves the byte) + + setDMPEnabled(false); // disable DMP for compatibility with the MPU6050 library +/* + dmpPacketSize += 16;//DMP_FEATURE_6X_LP_QUAT + dmpPacketSize += 6;//DMP_FEATURE_SEND_RAW_ACCEL + dmpPacketSize += 6;//DMP_FEATURE_SEND_RAW_GYRO +*/ + dmpPacketSize = 28; + return 0; +} + +bool MPU6050::dmpPacketAvailable() { + return getFIFOCount() >= dmpGetFIFOPacketSize(); +} + +// uint8_t MPU6050::dmpSetFIFORate(uint8_t fifoRate); +// uint8_t MPU6050::dmpGetFIFORate(); +// uint8_t MPU6050::dmpGetSampleStepSizeMS(); +// uint8_t MPU6050::dmpGetSampleFrequency(); +// int32_t MPU6050::dmpDecodeTemperature(int8_t tempReg); + +//uint8_t MPU6050::dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); +//uint8_t MPU6050::dmpUnregisterFIFORateProcess(inv_obj_func func); +//uint8_t MPU6050::dmpRunFIFORateProcesses(); + +// uint8_t MPU6050::dmpSendQuaternion(uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendPacketNumber(uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050::dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); + +uint8_t MPU6050::dmpGetAccel(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[16] << 8) | packet[17]); + data[1] = (((uint32_t)packet[18] << 8) | packet[19]); + data[2] = (((uint32_t)packet[20] << 8) | packet[21]); + return 0; +} +uint8_t MPU6050::dmpGetAccel(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (packet[16] << 8) | packet[17]; + data[1] = (packet[18] << 8) | packet[19]; + data[2] = (packet[20] << 8) | packet[21]; + return 0; +} +uint8_t MPU6050::dmpGetAccel(VectorInt16 *v, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + v -> x = (packet[16] << 8) | packet[17]; + v -> y = (packet[18] << 8) | packet[19]; + v -> z = (packet[20] << 8) | packet[21]; + return 0; +} +uint8_t MPU6050::dmpGetQuaternion(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[0] << 24) | ((uint32_t)packet[1] << 16) | ((uint32_t)packet[2] << 8) | packet[3]); + data[1] = (((uint32_t)packet[4] << 24) | ((uint32_t)packet[5] << 16) | ((uint32_t)packet[6] << 8) | packet[7]); + data[2] = (((uint32_t)packet[8] << 24) | ((uint32_t)packet[9] << 16) | ((uint32_t)packet[10] << 8) | packet[11]); + data[3] = (((uint32_t)packet[12] << 24) | ((uint32_t)packet[13] << 16) | ((uint32_t)packet[14] << 8) | packet[15]); + return 0; +} +uint8_t MPU6050::dmpGetQuaternion(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = ((packet[0] << 8) | packet[1]); + data[1] = ((packet[4] << 8) | packet[5]); + data[2] = ((packet[8] << 8) | packet[9]); + data[3] = ((packet[12] << 8) | packet[13]); + return 0; +} +uint8_t MPU6050::dmpGetQuaternion(Quaternion *q, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + int16_t qI[4]; + uint8_t status = dmpGetQuaternion(qI, packet); + if (status == 0) { + q -> w = (float)qI[0] / 16384.0f; + q -> x = (float)qI[1] / 16384.0f; + q -> y = (float)qI[2] / 16384.0f; + q -> z = (float)qI[3] / 16384.0f; + return 0; + } + return status; // int16 return value, indicates error if this line is reached +} +// uint8_t MPU6050::dmpGet6AxisQuaternion(long *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetRelativeQuaternion(long *data, const uint8_t* packet); +uint8_t MPU6050::dmpGetGyro(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[22] << 8) | packet[23]); + data[1] = (((uint32_t)packet[24] << 8) | packet[25]); + data[2] = (((uint32_t)packet[26] << 8) | packet[27]); + return 0; +} +uint8_t MPU6050::dmpGetGyro(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (packet[22] << 8) | packet[23]; + data[1] = (packet[24] << 8) | packet[25]; + data[2] = (packet[26] << 8) | packet[27]; + return 0; +} +uint8_t MPU6050::dmpGetGyro(VectorInt16 *v, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + v -> x = (packet[22] << 8) | packet[23]; + v -> y = (packet[24] << 8) | packet[25]; + v -> z = (packet[26] << 8) | packet[27]; + return 0; +} +// uint8_t MPU6050::dmpSetLinearAccelFilterCoefficient(float coef); +// uint8_t MPU6050::dmpGetLinearAccel(long *data, const uint8_t* packet); +uint8_t MPU6050::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) { + // get rid of the gravity component (+1g = +16384 in standard DMP FIFO packet, sensitivity is 2g) + v -> x = vRaw -> x - gravity -> x*16384; + v -> y = vRaw -> y - gravity -> y*16384; + v -> z = vRaw -> z - gravity -> z*16384; + return 0; +} +// uint8_t MPU6050::dmpGetLinearAccelInWorld(long *data, const uint8_t* packet); +uint8_t MPU6050::dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q) { + // rotate measured 3D acceleration vector into original state + // frame of reference based on orientation quaternion + memcpy(v, vReal, sizeof(VectorInt16)); + v -> rotate(q); + return 0; +} +// uint8_t MPU6050::dmpGetGyroAndAccelSensor(long *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetGyroSensor(long *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetControlData(long *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetTemperature(long *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetGravity(long *data, const uint8_t* packet); +uint8_t MPU6050::dmpGetGravity(int16_t *data, const uint8_t* packet) { + /* +1g corresponds to +16384, sensitivity is 2g. */ + int16_t qI[4]; + uint8_t status = dmpGetQuaternion(qI, packet); + data[0] = ((int32_t)qI[1] * qI[3] - (int32_t)qI[0] * qI[2]) / 16384; + data[1] = ((int32_t)qI[0] * qI[1] + (int32_t)qI[2] * qI[3]) / 16384; + data[2] = ((int32_t)qI[0] * qI[0] - (int32_t)qI[1] * qI[1] + - (int32_t)qI[2] * qI[2] + (int32_t)qI[3] * qI[3]) / (int32_t)(2 * 16384L); + return status; +} + +uint8_t MPU6050::dmpGetGravity(VectorFloat *v, Quaternion *q) { + v -> x = 2 * (q -> x*q -> z - q -> w*q -> y); + v -> y = 2 * (q -> w*q -> x + q -> y*q -> z); + v -> z = q -> w*q -> w - q -> x*q -> x - q -> y*q -> y + q -> z*q -> z; + return 0; +} +// uint8_t MPU6050::dmpGetUnquantizedAccel(long *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetQuantizedAccel(long *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetExternalSensorData(long *data, int size, const uint8_t* packet); +// uint8_t MPU6050::dmpGetEIS(long *data, const uint8_t* packet); + +uint8_t MPU6050::dmpGetEuler(float *data, Quaternion *q) { + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); // psi + data[1] = -asin(2*q -> x*q -> z + 2*q -> w*q -> y); // theta + data[2] = atan2(2*q -> y*q -> z - 2*q -> w*q -> x, 2*q -> w*q -> w + 2*q -> z*q -> z - 1); // phi + return 0; +} + +#ifdef USE_OLD_DMPGETYAWPITCHROLL +uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) { + // yaw: (about Z axis) + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); + // pitch: (nose up/down, about Y axis) + data[1] = atan(gravity -> x / sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z)); + // roll: (tilt left/right, about X axis) + data[2] = atan(gravity -> y / sqrt(gravity -> x*gravity -> x + gravity -> z*gravity -> z)); + return 0; +} +#else +uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) { + // yaw: (about Z axis) + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); + // pitch: (nose up/down, about Y axis) + data[1] = atan2(gravity -> x , sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z)); + // roll: (tilt left/right, about X axis) + data[2] = atan2(gravity -> y , gravity -> z); + if (gravity -> z < 0) { + if(data[1] > 0) { + data[1] = PI - data[1]; + } else { + data[1] = -PI - data[1]; + } + } + return 0; +} +#endif + +// uint8_t MPU6050::dmpGetAccelFloat(float *data, const uint8_t* packet); +// uint8_t MPU6050::dmpGetQuaternionFloat(float *data, const uint8_t* packet); + +uint8_t MPU6050::dmpProcessFIFOPacket(const unsigned char *dmpData) { + (void)dmpData; // unused parameter + /*for (uint8_t k = 0; k < dmpPacketSize; k++) { + if (dmpData[k] < 0x10) Serial.print("0"); + Serial.print(dmpData[k], HEX); + Serial.print(" "); + } + Serial.print("\n");*/ + //Serial.println((uint16_t)dmpPacketBuffer); + return 0; +} +uint8_t MPU6050::dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed) { + uint8_t status; + uint8_t buf[dmpPacketSize]; + for (uint8_t i = 0; i < numPackets; i++) { + // read packet from FIFO + getFIFOBytes(buf, dmpPacketSize); + + // process packet + if ((status = dmpProcessFIFOPacket(buf)) > 0) return status; + + // increment external process count variable, if supplied + if (processed != 0) (*processed)++; + } + return 0; +} + +// uint8_t MPU6050::dmpSetFIFOProcessedCallback(void (*func) (void)); + +// uint8_t MPU6050::dmpInitFIFOParam(); +// uint8_t MPU6050::dmpCloseFIFO(); +// uint8_t MPU6050::dmpSetGyroDataSource(uint_fast8_t source); +// uint8_t MPU6050::dmpDecodeQuantizedAccel(); +// uint32_t MPU6050::dmpGetGyroSumOfSquare(); +// uint32_t MPU6050::dmpGetAccelSumOfSquare(); +// void MPU6050::dmpOverrideQuaternion(long *q); +uint16_t MPU6050::dmpGetFIFOPacketSize() { + return dmpPacketSize; +} + + + +uint8_t MPU6050::dmpGetCurrentFIFOPacket(uint8_t *data) { // overflow proof + return(GetCurrentFIFOPacket(data, dmpPacketSize)); +} diff --git a/MPU6050_6Axis_MotionApps612.h b/MPU6050_6Axis_MotionApps612.h new file mode 100644 index 0000000..56e3bb3 --- /dev/null +++ b/MPU6050_6Axis_MotionApps612.h @@ -0,0 +1,153 @@ +// I2Cdev library collection - MPU6050 I2C device class +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 10/3/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021/09/27 - split implementations out of header files, finally +// ... - ongoing debug release + +// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE +// DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF +// YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING. + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#ifndef _MPU6050_6AXIS_MOTIONAPPS612_H_ +#define _MPU6050_6AXIS_MOTIONAPPS612_H_ + +// take ownership of the "MPU6050" typedef +#define I2CDEVLIB_MPU6050_TYPEDEF + +#include "MPU6050.h" + +class MPU6050_6Axis_MotionApps612 : public MPU6050_Base { + public: + MPU6050_6Axis_MotionApps612(uint8_t address=MPU6050_DEFAULT_ADDRESS, void *wireObj=0) : MPU6050_Base(address, wireObj) { } + + uint8_t dmpInitialize(); + bool dmpPacketAvailable(); + + uint8_t dmpSetFIFORate(uint8_t fifoRate); + uint8_t dmpGetFIFORate(); + uint8_t dmpGetSampleStepSizeMS(); + uint8_t dmpGetSampleFrequency(); + int32_t dmpDecodeTemperature(int8_t tempReg); + + // Register callbacks after a packet of FIFO data is processed + //uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); + //uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func); + uint8_t dmpRunFIFORateProcesses(); + + // Setup FIFO for various output + uint8_t dmpSendQuaternion(uint_fast16_t accuracy); + uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendPacketNumber(uint_fast16_t accuracy); + uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); + + // Get Fixed Point data from FIFO + uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpSetLinearAccelFilterCoefficient(float coef); + uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity); + uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q); + uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q); + uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0); + uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0); + + uint8_t dmpGetEuler(float *data, Quaternion *q); + uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity); + + // Get Floating Point data from FIFO + uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0); + + uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData); + uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL); + + uint8_t dmpSetFIFOProcessedCallback(void (*func) (void)); + + uint8_t dmpInitFIFOParam(); + uint8_t dmpCloseFIFO(); + uint8_t dmpSetGyroDataSource(uint8_t source); + uint8_t dmpDecodeQuantizedAccel(); + uint32_t dmpGetGyroSumOfSquare(); + uint32_t dmpGetAccelSumOfSquare(); + void dmpOverrideQuaternion(long *q); + uint16_t dmpGetFIFOPacketSize(); + uint8_t dmpGetCurrentFIFOPacket(uint8_t *data); // overflow proof + + private: + uint8_t *dmpPacketBuffer; + uint16_t dmpPacketSize; +}; + +typedef MPU6050_6Axis_MotionApps612 MPU6050; + +#endif /* _MPU6050_6AXIS_MOTIONAPPS612_H_ */ diff --git a/MPU6050_9Axis_MotionApps41.cpp b/MPU6050_9Axis_MotionApps41.cpp new file mode 100644 index 0000000..d85d74a --- /dev/null +++ b/MPU6050_9Axis_MotionApps41.cpp @@ -0,0 +1,887 @@ +// I2Cdev library collection - MPU6050 I2C device class, 9-axis MotionApps 4.1 implementation +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 6/18/2012 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021/09/27 - split implementations out of header files, finally + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2021 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +// MotionApps 4.1 DMP implementation, built using the MPU-9150 "MotionFit" board +#define MPU6050_INCLUDE_DMP_MOTIONAPPS41 + +#include "MPU6050_9Axis_MotionApps41.h" + +// Tom Carpenter's conditional PROGMEM code +// http://forum.arduino.cc/index.php?topic=129407.0 +#ifdef __AVR__ + #include +#elif defined(ESP32) + #include +#else + // Teensy 3.0 library conditional PROGMEM code from Paul Stoffregen + #ifndef __PGMSPACE_H_ + #define __PGMSPACE_H_ 1 + #include + + #define PROGMEM + #define PGM_P const char * + #define PSTR(str) (str) + #define F(x) x + + typedef void prog_void; + typedef char prog_char; + //typedef unsigned char prog_uchar; + typedef int8_t prog_int8_t; + typedef uint8_t prog_uint8_t; + typedef int16_t prog_int16_t; + typedef uint16_t prog_uint16_t; + typedef int32_t prog_int32_t; + typedef uint32_t prog_uint32_t; + + #define strcpy_P(dest, src) strcpy((dest), (src)) + #define strcat_P(dest, src) strcat((dest), (src)) + #define strcmp_P(a, b) strcmp((a), (b)) + + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + #define pgm_read_word(addr) (*(const unsigned short *)(addr)) + #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) + #define pgm_read_float(addr) (*(const float *)(addr)) + + #define pgm_read_byte_near(addr) pgm_read_byte(addr) + #define pgm_read_word_near(addr) pgm_read_word(addr) + #define pgm_read_dword_near(addr) pgm_read_dword(addr) + #define pgm_read_float_near(addr) pgm_read_float(addr) + #define pgm_read_byte_far(addr) pgm_read_byte(addr) + #define pgm_read_word_far(addr) pgm_read_word(addr) + #define pgm_read_dword_far(addr) pgm_read_dword(addr) + #define pgm_read_float_far(addr) pgm_read_float(addr) + #endif +#endif + +// NOTE! Enabling DEBUG adds about 3.3kB to the flash program size. +// Debug output is now working even on ATMega328P MCUs (e.g. Arduino Uno) +// after moving string constants to flash memory storage using the F() +// compiler macro (Arduino IDE 1.0+ required). + +//#define DEBUG +#ifdef DEBUG + #define DEBUG_PRINT(x) Serial.print(x) + #define DEBUG_PRINTF(x, y) Serial.print(x, y) + #define DEBUG_PRINTLN(x) Serial.println(x) + #define DEBUG_PRINTLNF(x, y) Serial.println(x, y) +#else + #define DEBUG_PRINT(x) + #define DEBUG_PRINTF(x, y) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTLNF(x, y) +#endif + +#define MPU6050_DMP_CODE_SIZE 1962 // dmpMemory[] +#define MPU6050_DMP_CONFIG_SIZE 232 // dmpConfig[] +#define MPU6050_DMP_UPDATES_SIZE 140 // dmpUpdates[] + +/* ================================================================================================ * + | Default MotionApps v4.1 48-byte FIFO packet structure: | + | | + | [QUAT W][ ][QUAT X][ ][QUAT Y][ ][QUAT Z][ ][GYRO X][ ][GYRO Y][ ] | + | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | + | | + | [GYRO Z][ ][MAG X ][MAG Y ][MAG Z ][ACC X ][ ][ACC Y ][ ][ACC Z ][ ][ ] | + | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | + * ================================================================================================ */ + +// this block of memory gets written to the MPU on start-up, and it seems +// to be volatile memory, so it has to be done each time (it only takes ~1 +// second though) +static const unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] PROGMEM = { + // bank 0, 256 bytes + 0xFB, 0x00, 0x00, 0x3E, 0x00, 0x0B, 0x00, 0x36, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, 0xFA, 0x80, 0x00, 0x0B, 0x12, 0x82, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x00, 0x00, 0xFF, 0xFF, 0x45, 0x81, 0xFF, 0xFF, 0xFA, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7F, 0xFF, 0xFF, 0xFE, 0x80, 0x01, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3E, 0x03, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xCA, 0xE3, 0x09, 0x3E, 0x80, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x41, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x2A, 0x00, 0x00, 0x16, 0x55, 0x00, 0x00, 0x21, 0x82, + 0xFD, 0x87, 0x26, 0x50, 0xFD, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x05, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x6F, 0x00, 0x02, 0x65, 0x32, 0x00, 0x00, 0x5E, 0xC0, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFB, 0x8C, 0x6F, 0x5D, 0xFD, 0x5D, 0x08, 0xD9, 0x00, 0x7C, 0x73, 0x3B, 0x00, 0x6C, 0x12, 0xCC, + 0x32, 0x00, 0x13, 0x9D, 0x32, 0x00, 0xD0, 0xD6, 0x32, 0x00, 0x08, 0x00, 0x40, 0x00, 0x01, 0xF4, + 0xFF, 0xE6, 0x80, 0x79, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xD6, 0x00, 0x00, 0x27, 0x10, + + // bank 1, 256 bytes + 0xFB, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFA, 0x36, 0xFF, 0xBC, 0x30, 0x8E, 0x00, 0x05, 0xFB, 0xF0, 0xFF, 0xD9, 0x5B, 0xC8, + 0xFF, 0xD0, 0x9A, 0xBE, 0x00, 0x00, 0x10, 0xA9, 0xFF, 0xF4, 0x1E, 0xB2, 0x00, 0xCE, 0xBB, 0xF7, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x0C, + 0xFF, 0xC2, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xCF, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x3F, 0x68, 0xB6, 0x79, 0x35, 0x28, 0xBC, 0xC6, 0x7E, 0xD1, 0x6C, + 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x6A, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x25, 0x4D, 0x00, 0x2F, 0x70, 0x6D, 0x00, 0x00, 0x05, 0xAE, 0x00, 0x0C, 0x02, 0xD0, + + // bank 2, 256 bytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0xFF, 0xEF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x78, 0xA2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // bank 3, 256 bytes + 0xD8, 0xDC, 0xF4, 0xD8, 0xB9, 0xAB, 0xF3, 0xF8, 0xFA, 0xF1, 0xBA, 0xA2, 0xDE, 0xB2, 0xB8, 0xB4, + 0xA8, 0x81, 0x98, 0xF7, 0x4A, 0x90, 0x7F, 0x91, 0x6A, 0xF3, 0xF9, 0xDB, 0xA8, 0xF9, 0xB0, 0xBA, + 0xA0, 0x80, 0xF2, 0xCE, 0x81, 0xF3, 0xC2, 0xF1, 0xC1, 0xF2, 0xC3, 0xF3, 0xCC, 0xA2, 0xB2, 0x80, + 0xF1, 0xC6, 0xD8, 0x80, 0xBA, 0xA7, 0xDF, 0xDF, 0xDF, 0xF2, 0xA7, 0xC3, 0xCB, 0xC5, 0xB6, 0xF0, + 0x87, 0xA2, 0x94, 0x24, 0x48, 0x70, 0x3C, 0x95, 0x40, 0x68, 0x34, 0x58, 0x9B, 0x78, 0xA2, 0xF1, + 0x83, 0x92, 0x2D, 0x55, 0x7D, 0xD8, 0xB1, 0xB4, 0xB8, 0xA1, 0xD0, 0x91, 0x80, 0xF2, 0x70, 0xF3, + 0x70, 0xF2, 0x7C, 0x80, 0xA8, 0xF1, 0x01, 0xB0, 0x98, 0x87, 0xD9, 0x43, 0xD8, 0x86, 0xC9, 0x88, + 0xBA, 0xA1, 0xF2, 0x0E, 0xB8, 0x97, 0x80, 0xF1, 0xA9, 0xDF, 0xDF, 0xDF, 0xAA, 0xDF, 0xDF, 0xDF, + 0xF2, 0xAA, 0xC5, 0xCD, 0xC7, 0xA9, 0x0C, 0xC9, 0x2C, 0x97, 0x97, 0x97, 0x97, 0xF1, 0xA9, 0x89, + 0x26, 0x46, 0x66, 0xB0, 0xB4, 0xBA, 0x80, 0xAC, 0xDE, 0xF2, 0xCA, 0xF1, 0xB2, 0x8C, 0x02, 0xA9, + 0xB6, 0x98, 0x00, 0x89, 0x0E, 0x16, 0x1E, 0xB8, 0xA9, 0xB4, 0x99, 0x2C, 0x54, 0x7C, 0xB0, 0x8A, + 0xA8, 0x96, 0x36, 0x56, 0x76, 0xF1, 0xB9, 0xAF, 0xB4, 0xB0, 0x83, 0xC0, 0xB8, 0xA8, 0x97, 0x11, + 0xB1, 0x8F, 0x98, 0xB9, 0xAF, 0xF0, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xF1, 0xA3, 0x29, 0x55, + 0x7D, 0xAF, 0x83, 0xB5, 0x93, 0xF0, 0x00, 0x28, 0x50, 0xF5, 0xBA, 0xAD, 0x8F, 0x9F, 0x28, 0x54, + 0x7C, 0xB9, 0xF1, 0xA3, 0x86, 0x9F, 0x61, 0xA6, 0xDA, 0xDE, 0xDF, 0xDB, 0xB2, 0xB6, 0x8E, 0x9D, + 0xAE, 0xF5, 0x60, 0x68, 0x70, 0xB1, 0xB5, 0xF1, 0xDA, 0xA6, 0xDF, 0xD9, 0xA6, 0xFA, 0xA3, 0x86, + + // bank 4, 256 bytes + 0x96, 0xDB, 0x31, 0xA6, 0xD9, 0xF8, 0xDF, 0xBA, 0xA6, 0x8F, 0xC2, 0xC5, 0xC7, 0xB2, 0x8C, 0xC1, + 0xB8, 0xA2, 0xDF, 0xDF, 0xDF, 0xA3, 0xDF, 0xDF, 0xDF, 0xD8, 0xD8, 0xF1, 0xB8, 0xA8, 0xB2, 0x86, + 0xB4, 0x98, 0x0D, 0x35, 0x5D, 0xB8, 0xAA, 0x98, 0xB0, 0x87, 0x2D, 0x35, 0x3D, 0xB2, 0xB6, 0xBA, + 0xAF, 0x8C, 0x96, 0x19, 0x8F, 0x9F, 0xA7, 0x0E, 0x16, 0x1E, 0xB4, 0x9A, 0xB8, 0xAA, 0x87, 0x2C, + 0x54, 0x7C, 0xB9, 0xA3, 0xDE, 0xDF, 0xDF, 0xA3, 0xB1, 0x80, 0xF2, 0xC4, 0xCD, 0xC9, 0xF1, 0xB8, + 0xA9, 0xB4, 0x99, 0x83, 0x0D, 0x35, 0x5D, 0x89, 0xB9, 0xA3, 0x2D, 0x55, 0x7D, 0xB5, 0x93, 0xA3, + 0x0E, 0x16, 0x1E, 0xA9, 0x2C, 0x54, 0x7C, 0xB8, 0xB4, 0xB0, 0xF1, 0x97, 0x83, 0xA8, 0x11, 0x84, + 0xA5, 0x09, 0x98, 0xA3, 0x83, 0xF0, 0xDA, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xD8, 0xF1, 0xA5, + 0x29, 0x55, 0x7D, 0xA5, 0x85, 0x95, 0x02, 0x1A, 0x2E, 0x3A, 0x56, 0x5A, 0x40, 0x48, 0xF9, 0xF3, + 0xA3, 0xD9, 0xF8, 0xF0, 0x98, 0x83, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0x97, 0x82, 0xA8, 0xF1, + 0x11, 0xF0, 0x98, 0xA2, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xDA, 0xF3, 0xDE, 0xD8, 0x83, 0xA5, + 0x94, 0x01, 0xD9, 0xA3, 0x02, 0xF1, 0xA2, 0xC3, 0xC5, 0xC7, 0xD8, 0xF1, 0x84, 0x92, 0xA2, 0x4D, + 0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32, 0xD8, 0x50, 0x71, 0xD9, + 0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8, 0x78, 0x93, 0xA3, 0x4D, + 0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32, 0xD8, 0x50, 0x71, 0xD9, + 0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8, 0x78, 0xA8, 0x8A, 0x9A, + + // bank 5, 256 bytes + 0xF0, 0x28, 0x50, 0x78, 0x9E, 0xF3, 0x88, 0x18, 0xF1, 0x9F, 0x1D, 0x98, 0xA8, 0xD9, 0x08, 0xD8, + 0xC8, 0x9F, 0x12, 0x9E, 0xF3, 0x15, 0xA8, 0xDA, 0x12, 0x10, 0xD8, 0xF1, 0xAF, 0xC8, 0x97, 0x87, + 0x34, 0xB5, 0xB9, 0x94, 0xA4, 0x21, 0xF3, 0xD9, 0x22, 0xD8, 0xF2, 0x2D, 0xF3, 0xD9, 0x2A, 0xD8, + 0xF2, 0x35, 0xF3, 0xD9, 0x32, 0xD8, 0x81, 0xA4, 0x60, 0x60, 0x61, 0xD9, 0x61, 0xD8, 0x6C, 0x68, + 0x69, 0xD9, 0x69, 0xD8, 0x74, 0x70, 0x71, 0xD9, 0x71, 0xD8, 0xB1, 0xA3, 0x84, 0x19, 0x3D, 0x5D, + 0xA3, 0x83, 0x1A, 0x3E, 0x5E, 0x93, 0x10, 0x30, 0x81, 0x10, 0x11, 0xB8, 0xB0, 0xAF, 0x8F, 0x94, + 0xF2, 0xDA, 0x3E, 0xD8, 0xB4, 0x9A, 0xA8, 0x87, 0x29, 0xDA, 0xF8, 0xD8, 0x87, 0x9A, 0x35, 0xDA, + 0xF8, 0xD8, 0x87, 0x9A, 0x3D, 0xDA, 0xF8, 0xD8, 0xB1, 0xB9, 0xA4, 0x98, 0x85, 0x02, 0x2E, 0x56, + 0xA5, 0x81, 0x00, 0x0C, 0x14, 0xA3, 0x97, 0xB0, 0x8A, 0xF1, 0x2D, 0xD9, 0x28, 0xD8, 0x4D, 0xD9, + 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x84, 0x0D, 0xDA, 0x0E, 0xD8, 0xA3, 0x29, 0x83, 0xDA, + 0x2C, 0x0E, 0xD8, 0xA3, 0x84, 0x49, 0x83, 0xDA, 0x2C, 0x4C, 0x0E, 0xD8, 0xB8, 0xB0, 0x97, 0x86, + 0xA8, 0x31, 0x9B, 0x06, 0x99, 0x07, 0xAB, 0x97, 0x28, 0x88, 0x9B, 0xF0, 0x0C, 0x20, 0x14, 0x40, + 0xB9, 0xA3, 0x8A, 0xC3, 0xC5, 0xC7, 0x9A, 0xA3, 0x28, 0x50, 0x78, 0xF1, 0xB5, 0x93, 0x01, 0xD9, + 0xDF, 0xDF, 0xDF, 0xD8, 0xB8, 0xB4, 0xA8, 0x8C, 0x9C, 0xF0, 0x04, 0x28, 0x51, 0x79, 0x1D, 0x30, + 0x14, 0x38, 0xB2, 0x82, 0xAB, 0xD0, 0x98, 0x2C, 0x50, 0x50, 0x78, 0x78, 0x9B, 0xF1, 0x1A, 0xB0, + 0xF0, 0xB1, 0x83, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0xB0, 0x8B, 0x29, 0x51, 0x79, 0xB1, 0x83, 0x24, + + // bank 6, 256 bytes + 0x70, 0x59, 0xB0, 0x8B, 0x20, 0x58, 0x71, 0xB1, 0x83, 0x44, 0x69, 0x38, 0xB0, 0x8B, 0x39, 0x40, + 0x68, 0xB1, 0x83, 0x64, 0x48, 0x31, 0xB0, 0x8B, 0x30, 0x49, 0x60, 0xA5, 0x88, 0x20, 0x09, 0x71, + 0x58, 0x44, 0x68, 0x11, 0x39, 0x64, 0x49, 0x30, 0x19, 0xF1, 0xAC, 0x00, 0x2C, 0x54, 0x7C, 0xF0, + 0x8C, 0xA8, 0x04, 0x28, 0x50, 0x78, 0xF1, 0x88, 0x97, 0x26, 0xA8, 0x59, 0x98, 0xAC, 0x8C, 0x02, + 0x26, 0x46, 0x66, 0xF0, 0x89, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69, 0x38, + 0x64, 0x48, 0x31, 0xA9, 0x88, 0x09, 0x20, 0x59, 0x70, 0xAB, 0x11, 0x38, 0x40, 0x69, 0xA8, 0x19, + 0x31, 0x48, 0x60, 0x8C, 0xA8, 0x3C, 0x41, 0x5C, 0x20, 0x7C, 0x00, 0xF1, 0x87, 0x98, 0x19, 0x86, + 0xA8, 0x6E, 0x76, 0x7E, 0xA9, 0x99, 0x88, 0x2D, 0x55, 0x7D, 0x9E, 0xB9, 0xA3, 0x8A, 0x22, 0x8A, + 0x6E, 0x8A, 0x56, 0x8A, 0x5E, 0x9F, 0xB1, 0x83, 0x06, 0x26, 0x46, 0x66, 0x0E, 0x2E, 0x4E, 0x6E, + 0x9D, 0xB8, 0xAD, 0x00, 0x2C, 0x54, 0x7C, 0xF2, 0xB1, 0x8C, 0xB4, 0x99, 0xB9, 0xA3, 0x2D, 0x55, + 0x7D, 0x81, 0x91, 0xAC, 0x38, 0xAD, 0x3A, 0xB5, 0x83, 0x91, 0xAC, 0x2D, 0xD9, 0x28, 0xD8, 0x4D, + 0xD9, 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0x8C, 0x9D, 0xAE, 0x29, 0xD9, 0x04, 0xAE, 0xD8, 0x51, + 0xD9, 0x04, 0xAE, 0xD8, 0x79, 0xD9, 0x04, 0xD8, 0x81, 0xF3, 0x9D, 0xAD, 0x00, 0x8D, 0xAE, 0x19, + 0x81, 0xAD, 0xD9, 0x01, 0xD8, 0xF2, 0xAE, 0xDA, 0x26, 0xD8, 0x8E, 0x91, 0x29, 0x83, 0xA7, 0xD9, + 0xAD, 0xAD, 0xAD, 0xAD, 0xF3, 0x2A, 0xD8, 0xD8, 0xF1, 0xB0, 0xAC, 0x89, 0x91, 0x3E, 0x5E, 0x76, + 0xF3, 0xAC, 0x2E, 0x2E, 0xF1, 0xB1, 0x8C, 0x5A, 0x9C, 0xAC, 0x2C, 0x28, 0x28, 0x28, 0x9C, 0xAC, + + // bank 7, 170 bytes (remainder) + 0x30, 0x18, 0xA8, 0x98, 0x81, 0x28, 0x34, 0x3C, 0x97, 0x24, 0xA7, 0x28, 0x34, 0x3C, 0x9C, 0x24, + 0xF2, 0xB0, 0x89, 0xAC, 0x91, 0x2C, 0x4C, 0x6C, 0x8A, 0x9B, 0x2D, 0xD9, 0xD8, 0xD8, 0x51, 0xD9, + 0xD8, 0xD8, 0x79, 0xD9, 0xD8, 0xD8, 0xF1, 0x9E, 0x88, 0xA3, 0x31, 0xDA, 0xD8, 0xD8, 0x91, 0x2D, + 0xD9, 0x28, 0xD8, 0x4D, 0xD9, 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x83, 0x93, 0x35, 0x3D, + 0x80, 0x25, 0xDA, 0xD8, 0xD8, 0x85, 0x69, 0xDA, 0xD8, 0xD8, 0xB4, 0x93, 0x81, 0xA3, 0x28, 0x34, + 0x3C, 0xF3, 0xAB, 0x8B, 0xA3, 0x91, 0xB6, 0x09, 0xB4, 0xD9, 0xAB, 0xDE, 0xB0, 0x87, 0x9C, 0xB9, + 0xA3, 0xDD, 0xF1, 0xA3, 0xA3, 0xA3, 0xA3, 0x95, 0xF1, 0xA3, 0xA3, 0xA3, 0x9D, 0xF1, 0xA3, 0xA3, + 0xA3, 0xA3, 0xF2, 0xA3, 0xB4, 0x90, 0x80, 0xF2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xB2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xB0, 0x87, 0xB5, 0x99, 0xF1, 0xA3, 0xA3, + 0xA3, 0x98, 0xF1, 0xA3, 0xA3, 0xA3, 0xA3, 0x97, 0xA3, 0xA3, 0xA3, 0xA3, 0xF3, 0x9B, 0xA3, 0xA3, + 0xDC, 0xB9, 0xA7, 0xF1, 0x26, 0x26, 0x26, 0xD8, 0xD8, 0xFF +}; + +#ifndef MPU6050_DMP_FIFO_RATE_DIVISOR +#define MPU6050_DMP_FIFO_RATE_DIVISOR 0x03 +#endif + +static const unsigned char dmpConfig[MPU6050_DMP_CONFIG_SIZE] PROGMEM = { +// BANK OFFSET LENGTH [DATA] + 0x02, 0xEC, 0x04, 0x00, 0x47, 0x7D, 0x1A, // ? + 0x03, 0x82, 0x03, 0x4C, 0xCD, 0x6C, // FCFG_1 inv_set_gyro_calibration + 0x03, 0xB2, 0x03, 0x36, 0x56, 0x76, // FCFG_3 inv_set_gyro_calibration + 0x00, 0x68, 0x04, 0x02, 0xCA, 0xE3, 0x09, // D_0_104 inv_set_gyro_calibration + 0x01, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, // D_1_152 inv_set_accel_calibration + 0x03, 0x86, 0x03, 0x0C, 0xC9, 0x2C, // FCFG_2 inv_set_accel_calibration + 0x03, 0x90, 0x03, 0x26, 0x46, 0x66, // (continued)...FCFG_2 inv_set_accel_calibration + 0x00, 0x6C, 0x02, 0x40, 0x00, // D_0_108 inv_set_accel_calibration + + 0x02, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, // CPASS_MTX_00 inv_set_compass_calibration + 0x02, 0x44, 0x04, 0x40, 0x00, 0x00, 0x00, // CPASS_MTX_01 + 0x02, 0x48, 0x04, 0x00, 0x00, 0x00, 0x00, // CPASS_MTX_02 + 0x02, 0x4C, 0x04, 0x40, 0x00, 0x00, 0x00, // CPASS_MTX_10 + 0x02, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, // CPASS_MTX_11 + 0x02, 0x54, 0x04, 0x00, 0x00, 0x00, 0x00, // CPASS_MTX_12 + 0x02, 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, // CPASS_MTX_20 + 0x02, 0x5C, 0x04, 0x00, 0x00, 0x00, 0x00, // CPASS_MTX_21 + 0x02, 0xBC, 0x04, 0xC0, 0x00, 0x00, 0x00, // CPASS_MTX_22 + + 0x01, 0xEC, 0x04, 0x00, 0x00, 0x40, 0x00, // D_1_236 inv_apply_endian_accel + 0x03, 0x86, 0x06, 0x0C, 0xC9, 0x2C, 0x97, 0x97, 0x97, // FCFG_2 inv_set_mpu_sensors + 0x04, 0x22, 0x03, 0x0D, 0x35, 0x5D, // CFG_MOTION_BIAS inv_turn_on_bias_from_no_motion + 0x00, 0xA3, 0x01, 0x00, // ? + 0x04, 0x29, 0x04, 0x87, 0x2D, 0x35, 0x3D, // FCFG_5 inv_set_bias_update + 0x07, 0x62, 0x05, 0xF1, 0x20, 0x28, 0x30, 0x38, // CFG_8 inv_send_quaternion + 0x07, 0x9F, 0x01, 0x30, // CFG_16 inv_set_footer + 0x07, 0x67, 0x01, 0x9A, // CFG_GYRO_SOURCE inv_send_gyro + 0x07, 0x68, 0x04, 0xF1, 0x28, 0x30, 0x38, // CFG_9 inv_send_gyro -> inv_construct3_fifo + 0x07, 0x62, 0x05, 0xF1, 0x20, 0x28, 0x30, 0x38, // ? + 0x02, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, // ? + 0x07, 0x83, 0x06, 0xC2, 0xCA, 0xC4, 0xA3, 0xA3, 0xA3, // ? + // SPECIAL 0x01 = enable interrupts + 0x00, 0x00, 0x00, 0x01, // SET INT_ENABLE, SPECIAL INSTRUCTION + 0x07, 0xA7, 0x01, 0xFE, // ? + 0x07, 0x62, 0x05, 0xF1, 0x20, 0x28, 0x30, 0x38, // ? + 0x07, 0x67, 0x01, 0x9A, // ? + 0x07, 0x68, 0x04, 0xF1, 0x28, 0x30, 0x38, // CFG_12 inv_send_accel -> inv_construct3_fifo + 0x07, 0x8D, 0x04, 0xF1, 0x28, 0x30, 0x38, // ??? CFG_12 inv_send_mag -> inv_construct3_fifo + 0x02, 0x16, 0x02, 0x00, MPU6050_DMP_FIFO_RATE_DIVISOR // D_0_22 inv_set_fifo_rate + + // This very last 0x03 WAS a 0x09, which drops the FIFO rate down to 20 Hz. 0x07 is 25 Hz, + // 0x01 is 100Hz. Going faster than 100Hz (0x00=200Hz) tends to result in very noisy data. + // DMP output frequency is calculated easily using this equation: (200Hz / (1 + value)) + + // It is important to make sure the host processor can keep up with reading and processing + // the FIFO output at the desired rate. Handling FIFO overflow cleanly is also a good idea. +}; + +const unsigned char dmpUpdates[MPU6050_DMP_UPDATES_SIZE] PROGMEM = { + 0x01, 0xB2, 0x02, 0xFF, 0xF5, + 0x01, 0x90, 0x04, 0x0A, 0x0D, 0x97, 0xC0, + 0x00, 0xA3, 0x01, 0x00, + 0x04, 0x29, 0x04, 0x87, 0x2D, 0x35, 0x3D, + 0x01, 0x6A, 0x02, 0x06, 0x00, + 0x01, 0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x04, 0x40, 0x00, 0x00, 0x00, + 0x02, 0x60, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x02, 0x01, 0x20, + 0x01, 0x0A, 0x02, 0x00, 0x4E, + 0x01, 0x02, 0x02, 0xFE, 0xB3, + 0x02, 0x6C, 0x04, 0x00, 0x00, 0x00, 0x00, // READ + 0x02, 0x6C, 0x04, 0xFA, 0xFE, 0x00, 0x00, + 0x02, 0x60, 0x0C, 0xFF, 0xFF, 0xCB, 0x4D, 0x00, 0x01, 0x08, 0xC1, 0xFF, 0xFF, 0xBC, 0x2C, + 0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xF8, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xFC, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x04, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x04, 0x00, 0x40, 0x00, 0x00 +}; + +uint8_t MPU6050_9Axis_MotionApps41::dmpInitialize() { + // reset device + DEBUG_PRINTLN(F("\n\nResetting MPU6050...")); + reset(); + delay(30); // wait after reset + + // disable sleep mode + DEBUG_PRINTLN(F("Disabling sleep mode...")); + setSleepEnabled(false); + + // get MPU product ID + DEBUG_PRINTLN(F("Getting product ID...")); + //uint8_t productID = 0; //getProductID(); + DEBUG_PRINT(F("Product ID = ")); + DEBUG_PRINT(productID); + + // get MPU hardware revision + DEBUG_PRINTLN(F("Selecting user bank 16...")); + setMemoryBank(0x10, true, true); + DEBUG_PRINTLN(F("Selecting memory byte 6...")); + setMemoryStartAddress(0x06); + DEBUG_PRINTLN(F("Checking hardware revision...")); + uint8_t hwRevision = readMemoryByte(); + (void)hwRevision; // suppress unused variable compile warning + DEBUG_PRINT(F("Revision @ user[16][6] = ")); + DEBUG_PRINTLNF(hwRevision, HEX); + DEBUG_PRINTLN(F("Resetting memory bank selection to 0...")); + setMemoryBank(0, false, false); + + // check OTP bank valid + DEBUG_PRINTLN(F("Reading OTP bank valid flag...")); + uint8_t otpValid = getOTPBankValid(); + (void)otpValid; // suppress unused variable compile warning + DEBUG_PRINT(F("OTP bank is ")); + DEBUG_PRINTLN(otpValid ? F("valid!") : F("invalid!")); + + // get X/Y/Z gyro offsets + DEBUG_PRINTLN(F("Reading gyro offset values...")); + int8_t xgOffset = getXGyroOffset(); + int8_t ygOffset = getYGyroOffset(); + int8_t zgOffset = getZGyroOffset(); + DEBUG_PRINT(F("X gyro offset = ")); + DEBUG_PRINTLN(xgOffset); + DEBUG_PRINT(F("Y gyro offset = ")); + DEBUG_PRINTLN(ygOffset); + DEBUG_PRINT(F("Z gyro offset = ")); + DEBUG_PRINTLN(zgOffset); + + I2Cdev::readByte(devAddr, MPU6050_RA_USER_CTRL, buffer, I2Cdev::readTimeout, wireObj); // ? + + DEBUG_PRINTLN(F("Enabling interrupt latch, clear on any read, AUX bypass enabled")); + I2Cdev::writeByte(devAddr, MPU6050_RA_INT_PIN_CFG, 0x32, wireObj); + + // enable MPU AUX I2C bypass mode + //DEBUG_PRINTLN(F("Enabling AUX I2C bypass mode...")); + //setI2CBypassEnabled(true); + + DEBUG_PRINTLN(F("Setting magnetometer mode to power-down...")); + //mag -> setMode(0); + I2Cdev::writeByte(0x0E, 0x0A, 0x00, wireObj); + + DEBUG_PRINTLN(F("Setting magnetometer mode to fuse access...")); + //mag -> setMode(0x0F); + I2Cdev::writeByte(0x0E, 0x0A, 0x0F, wireObj); + + DEBUG_PRINTLN(F("Reading mag magnetometer factory calibration...")); + int8_t asax, asay, asaz; + //mag -> getAdjustment(&asax, &asay, &asaz); + I2Cdev::readBytes(0x0E, 0x10, 3, buffer, I2Cdev::readTimeout, wireObj); + asax = (int8_t)buffer[0]; + asay = (int8_t)buffer[1]; + asaz = (int8_t)buffer[2]; + (void)asax; // suppress unused variable compiler warning + (void)asay; // suppress unused variable compiler warning + (void)asaz; // suppress unused variable compiler warning + DEBUG_PRINT(F("Adjustment X/Y/Z = ")); + DEBUG_PRINT(asax); + DEBUG_PRINT(F(" / ")); + DEBUG_PRINT(asay); + DEBUG_PRINT(F(" / ")); + DEBUG_PRINTLN(asaz); + + DEBUG_PRINTLN(F("Setting magnetometer mode to power-down...")); + //mag -> setMode(0); + I2Cdev::writeByte(0x0E, 0x0A, 0x00, wireObj); + + // load DMP code into memory banks + DEBUG_PRINT(F("Writing DMP code to MPU memory banks (")); + DEBUG_PRINT(MPU6050_DMP_CODE_SIZE); + DEBUG_PRINTLN(F(" bytes)")); + if (writeProgMemoryBlock(dmpMemory, MPU6050_DMP_CODE_SIZE)) { + DEBUG_PRINTLN(F("Success! DMP code written and verified.")); + + DEBUG_PRINTLN(F("Configuring DMP and related settings...")); + + // write DMP configuration + DEBUG_PRINT(F("Writing DMP configuration to MPU memory banks (")); + DEBUG_PRINT(MPU6050_DMP_CONFIG_SIZE); + DEBUG_PRINTLN(F(" bytes in config def)")); + if (writeProgDMPConfigurationSet(dmpConfig, MPU6050_DMP_CONFIG_SIZE)) { + DEBUG_PRINTLN(F("Success! DMP configuration written and verified.")); + + DEBUG_PRINTLN(F("Setting DMP and FIFO_OFLOW interrupts enabled...")); + setIntEnabled(1< setMode(1); + I2Cdev::writeByte(0x0E, 0x0A, 0x01, wireObj); + + // setup AK8975 (0x0E) as Slave 0 in read mode + DEBUG_PRINTLN(F("Setting up AK8975 read slave 0...")); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV0_ADDR, 0x8E, wireObj); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV0_REG, 0x01, wireObj); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV0_CTRL, 0xDA, wireObj); + + // setup AK8975 (0x0E) as Slave 2 in write mode + DEBUG_PRINTLN(F("Setting up AK8975 write slave 2...")); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_ADDR, 0x0E, wireObj); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_REG, 0x0A, wireObj); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_CTRL, 0x81, wireObj); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_DO, 0x01, wireObj); + + // setup I2C timing/delay control + DEBUG_PRINTLN(F("Setting up slave access delay...")); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV4_CTRL, 0x18, wireObj); + I2Cdev::writeByte(0x68, MPU6050_RA_I2C_MST_DELAY_CTRL, 0x05, wireObj); + + // enable interrupts + DEBUG_PRINTLN(F("Enabling default interrupt behavior/no bypass...")); + I2Cdev::writeByte(0x68, MPU6050_RA_INT_PIN_CFG, 0x00, wireObj); + + // enable I2C master mode and reset DMP/FIFO + DEBUG_PRINTLN(F("Enabling I2C master mode...")); + I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0x20, wireObj); + DEBUG_PRINTLN(F("Resetting FIFO...")); + I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0x24, wireObj); + DEBUG_PRINTLN(F("Rewriting I2C master mode enabled because...I don't know")); + I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0x20, wireObj); + DEBUG_PRINTLN(F("Enabling and resetting DMP/FIFO...")); + I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0xE8, wireObj); + + DEBUG_PRINTLN(F("Writing final memory update 5/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 6/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 7/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 8/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 9/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 10/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 11/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + + DEBUG_PRINTLN(F("Reading final memory update 12/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + readMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + #ifdef DEBUG + DEBUG_PRINT(F("Read bytes: ")); + for (j = 0; j < 4; j++) { + DEBUG_PRINTF(dmpUpdate[3 + j], HEX); + DEBUG_PRINT(" "); + } + DEBUG_PRINTLN(""); + #endif + + DEBUG_PRINTLN(F("Writing final memory update 13/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 14/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 15/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 16/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + DEBUG_PRINTLN(F("Writing final memory update 17/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + + DEBUG_PRINTLN(F("Waiting for FIRO count >= 46...")); + while ((fifoCount = getFIFOCount()) < 46); + DEBUG_PRINTLN(F("Reading FIFO...")); + getFIFOBytes(fifoBuffer, (fifoCount < 128) ? fifoCount : 128); // safeguard only 128 bytes + DEBUG_PRINTLN(F("Reading interrupt status...")); + getIntStatus(); + + DEBUG_PRINTLN(F("Writing final memory update 18/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + + DEBUG_PRINTLN(F("Waiting for FIRO count >= 48...")); + while ((fifoCount = getFIFOCount()) < 48); + DEBUG_PRINTLN(F("Reading FIFO...")); + getFIFOBytes(fifoBuffer, (fifoCount < 128) ? fifoCount : 128); // safeguard only 128 bytes + DEBUG_PRINTLN(F("Reading interrupt status...")); + getIntStatus(); + DEBUG_PRINTLN(F("Waiting for FIRO count >= 48...")); + while ((fifoCount = getFIFOCount()) < 48); + DEBUG_PRINTLN(F("Reading FIFO...")); + getFIFOBytes(fifoBuffer, (fifoCount < 128) ? fifoCount : 128); // safeguard only 128 bytes + DEBUG_PRINTLN(F("Reading interrupt status...")); + getIntStatus(); + + DEBUG_PRINTLN(F("Writing final memory update 19/19 (function unknown)...")); + for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]); + writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]); + + DEBUG_PRINTLN(F("Disabling DMP (you turn it on later)...")); + setDMPEnabled(false); + + DEBUG_PRINTLN(F("Setting up internal 48-byte (default) DMP packet buffer...")); + dmpPacketSize = 48; + /*if ((dmpPacketBuffer = (uint8_t *)malloc(42)) == 0) { + return 3; // TODO: proper error code for no memory + }*/ + + DEBUG_PRINTLN(F("Resetting FIFO and clearing INT status one last time...")); + resetFIFO(); + getIntStatus(); + } else { + DEBUG_PRINTLN(F("ERROR! DMP configuration verification failed.")); + return 2; // configuration block loading failed + } + } else { + DEBUG_PRINTLN(F("ERROR! DMP code verification failed.")); + return 1; // main binary block loading failed + } + return 0; // success +} + +bool MPU6050_9Axis_MotionApps41::dmpPacketAvailable() { + return getFIFOCount() >= dmpGetFIFOPacketSize(); +} + +// uint8_t MPU6050_9Axis_MotionApps41::dmpSetFIFORate(uint8_t fifoRate); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetFIFORate(); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetSampleStepSizeMS(); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetSampleFrequency(); +// int32_t MPU6050_9Axis_MotionApps41::dmpDecodeTemperature(int8_t tempReg); + +//uint8_t MPU6050_9Axis_MotionApps41::dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); +//uint8_t MPU6050_9Axis_MotionApps41::dmpUnregisterFIFORateProcess(inv_obj_func func); +//uint8_t MPU6050_9Axis_MotionApps41::dmpRunFIFORateProcesses(); + +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendQuaternion(uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendPacketNumber(uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); + +uint8_t MPU6050_9Axis_MotionApps41::dmpGetAccel(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[34] << 24) | ((uint32_t)packet[35] << 16) | ((uint32_t)packet[36] << 8) | packet[37]); + data[1] = (((uint32_t)packet[38] << 24) | ((uint32_t)packet[39] << 16) | ((uint32_t)packet[40] << 8) | packet[41]); + data[2] = (((uint32_t)packet[42] << 24) | ((uint32_t)packet[43] << 16) | ((uint32_t)packet[44] << 8) | packet[45]); + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpGetAccel(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (packet[34] << 8) | packet[35]; + data[1] = (packet[38] << 8) | packet[39]; + data[2] = (packet[42] << 8) | packet[43]; + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpGetAccel(VectorInt16 *v, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + v -> x = (packet[34] << 8) | packet[35]; + v -> y = (packet[38] << 8) | packet[39]; + v -> z = (packet[42] << 8) | packet[43]; + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpGetQuaternion(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[0] << 24) | ((uint32_t)packet[1] << 16) | ((uint32_t)packet[2] << 8) | packet[3]); + data[1] = (((uint32_t)packet[4] << 24) | ((uint32_t)packet[5] << 16) | ((uint32_t)packet[6] << 8) | packet[7]); + data[2] = (((uint32_t)packet[8] << 24) | ((uint32_t)packet[9] << 16) | ((uint32_t)packet[10] << 8) | packet[11]); + data[3] = (((uint32_t)packet[12] << 24) | ((uint32_t)packet[13] << 16) | ((uint32_t)packet[14] << 8) | packet[15]); + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpGetQuaternion(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = ((packet[0] << 8) | packet[1]); + data[1] = ((packet[4] << 8) | packet[5]); + data[2] = ((packet[8] << 8) | packet[9]); + data[3] = ((packet[12] << 8) | packet[13]); + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpGetQuaternion(Quaternion *q, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + int16_t qI[4]; + uint8_t status = dmpGetQuaternion(qI, packet); + if (status == 0) { + q -> w = (float)qI[0] / 16384.0f; + q -> x = (float)qI[1] / 16384.0f; + q -> y = (float)qI[2] / 16384.0f; + q -> z = (float)qI[3] / 16384.0f; + return 0; + } + return status; // int16 return value, indicates error if this line is reached +} +// uint8_t MPU6050_9Axis_MotionApps41::dmpGet6AxisQuaternion(long *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetRelativeQuaternion(long *data, const uint8_t* packet); +uint8_t MPU6050_9Axis_MotionApps41::dmpGetGyro(int32_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (((uint32_t)packet[16] << 24) | ((uint32_t)packet[17] << 16) | ((uint32_t)packet[18] << 8) | packet[19]); + data[1] = (((uint32_t)packet[20] << 24) | ((uint32_t)packet[21] << 16) | ((uint32_t)packet[22] << 8) | packet[23]); + data[2] = (((uint32_t)packet[24] << 24) | ((uint32_t)packet[25] << 16) | ((uint32_t)packet[26] << 8) | packet[27]); + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpGetGyro(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (packet[16] << 8) | packet[17]; + data[1] = (packet[20] << 8) | packet[21]; + data[2] = (packet[24] << 8) | packet[25]; + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpGetMag(int16_t *data, const uint8_t* packet) { + // TODO: accommodate different arrangements of sent data (ONLY default supported now) + if (packet == 0) packet = dmpPacketBuffer; + data[0] = (packet[28] << 8) | packet[29]; + data[1] = (packet[30] << 8) | packet[31]; + data[2] = (packet[32] << 8) | packet[33]; + return 0; +} +// uint8_t MPU6050_9Axis_MotionApps41::dmpSetLinearAccelFilterCoefficient(float coef); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetLinearAccel(long *data, const uint8_t* packet); +uint8_t MPU6050_9Axis_MotionApps41::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) { + // get rid of the gravity component (+1g = +4096 in standard DMP FIFO packet) + v -> x = vRaw -> x - gravity -> x*4096; + v -> y = vRaw -> y - gravity -> y*4096; + v -> z = vRaw -> z - gravity -> z*4096; + return 0; +} +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetLinearAccelInWorld(long *data, const uint8_t* packet); +uint8_t MPU6050_9Axis_MotionApps41::dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q) { + // rotate measured 3D acceleration vector into original state + // frame of reference based on orientation quaternion + memcpy(v, vReal, sizeof(VectorInt16)); + v -> rotate(q); + return 0; +} +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetGyroAndAccelSensor(long *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetGyroSensor(long *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetControlData(long *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetTemperature(long *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetGravity(long *data, const uint8_t* packet); +uint8_t MPU6050_9Axis_MotionApps41::dmpGetGravity(int16_t *data, const uint8_t* packet) { + /* +1g corresponds to +8192, sensitivity is 2g. */ + int16_t qI[4]; + uint8_t status = dmpGetQuaternion(qI, packet); + data[0] = ((int32_t)qI[1] * qI[3] - (int32_t)qI[0] * qI[2]) / 16384; + data[1] = ((int32_t)qI[0] * qI[1] + (int32_t)qI[2] * qI[3]) / 16384; + data[2] = ((int32_t)qI[0] * qI[0] - (int32_t)qI[1] * qI[1] + - (int32_t)qI[2] * qI[2] + (int32_t)qI[3] * qI[3]) / (int32_t)(2 * 16384L); + return status; +} + +uint8_t MPU6050_9Axis_MotionApps41::dmpGetGravity(VectorFloat *v, Quaternion *q) { + v -> x = 2 * (q -> x*q -> z - q -> w*q -> y); + v -> y = 2 * (q -> w*q -> x + q -> y*q -> z); + v -> z = q -> w*q -> w - q -> x*q -> x - q -> y*q -> y + q -> z*q -> z; + return 0; +} +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetUnquantizedAccel(long *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetQuantizedAccel(long *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetExternalSensorData(long *data, int size, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetEIS(long *data, const uint8_t* packet); + +uint8_t MPU6050_9Axis_MotionApps41::dmpGetEuler(float *data, Quaternion *q) { + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); // psi + data[1] = -asin(2*q -> x*q -> z + 2*q -> w*q -> y); // theta + data[2] = atan2(2*q -> y*q -> z - 2*q -> w*q -> x, 2*q -> w*q -> w + 2*q -> z*q -> z - 1); // phi + return 0; +} + +#ifdef USE_OLD_DMPGETYAWPITCHROLL +uint8_t MPU6050_9Axis_MotionApps41::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) { + // yaw: (about Z axis) + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); + // pitch: (nose up/down, about Y axis) + data[1] = atan(gravity -> x / sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z)); + // roll: (tilt left/right, about X axis) + data[2] = atan(gravity -> y / sqrt(gravity -> x*gravity -> x + gravity -> z*gravity -> z)); + return 0; +} +#else +uint8_t MPU6050_9Axis_MotionApps41::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) { + // yaw: (about Z axis) + data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1); + // pitch: (nose up/down, about Y axis) + data[1] = atan2(gravity -> x , sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z)); + // roll: (tilt left/right, about X axis) + data[2] = atan2(gravity -> y , gravity -> z); + if(gravity->z<0) { + if(data[1]>0) { + data[1] = PI - data[1]; + } else { + data[1] = -PI - data[1]; + } + } + return 0; +} +#endif + +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetAccelFloat(float *data, const uint8_t* packet); +// uint8_t MPU6050_9Axis_MotionApps41::dmpGetQuaternionFloat(float *data, const uint8_t* packet); + +uint8_t MPU6050_9Axis_MotionApps41::dmpProcessFIFOPacket(const unsigned char *dmpData) { + (void)dmpData; // suppress unused variable compiler warning + /*for (uint8_t k = 0; k < dmpPacketSize; k++) { + if (dmpData[k] < 0x10) Serial.print("0"); + Serial.print(dmpData[k], HEX); + Serial.print(" "); + } + Serial.print("\n");*/ + //Serial.println((uint16_t)dmpPacketBuffer); + return 0; +} +uint8_t MPU6050_9Axis_MotionApps41::dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed) { + uint8_t status; + uint8_t buf[dmpPacketSize]; + for (uint8_t i = 0; i < numPackets; i++) { + // read packet from FIFO + getFIFOBytes(buf, dmpPacketSize); + + // process packet + if ((status = dmpProcessFIFOPacket(buf)) > 0) return status; + + // increment external process count variable, if supplied + if (processed != 0) (*processed)++; + } + return 0; +} + +// uint8_t MPU6050_9Axis_MotionApps41::dmpSetFIFOProcessedCallback(void (*func) (void)); + +// uint8_t MPU6050_9Axis_MotionApps41::dmpInitFIFOParam(); +// uint8_t MPU6050_9Axis_MotionApps41::dmpCloseFIFO(); +// uint8_t MPU6050_9Axis_MotionApps41::dmpSetGyroDataSource(uint_fast8_t source); +// uint8_t MPU6050_9Axis_MotionApps41::dmpDecodeQuantizedAccel(); +// uint32_t MPU6050_9Axis_MotionApps41::dmpGetGyroSumOfSquare(); +// uint32_t MPU6050_9Axis_MotionApps41::dmpGetAccelSumOfSquare(); +// void MPU6050_9Axis_MotionApps41::dmpOverrideQuaternion(long *q); +uint16_t MPU6050_9Axis_MotionApps41::dmpGetFIFOPacketSize() { + return dmpPacketSize; +} diff --git a/MPU6050_9Axis_MotionApps41.h b/MPU6050_9Axis_MotionApps41.h new file mode 100644 index 0000000..6d3576e --- /dev/null +++ b/MPU6050_9Axis_MotionApps41.h @@ -0,0 +1,153 @@ +// I2Cdev library collection - MPU6050 I2C device class +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 10/3/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2021/09/27 - split implementations out of header files, finally +// ... - ongoing debug release + +// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE +// DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF +// YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING. + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#ifndef _MPU6050_6AXIS_MOTIONAPPS41_H_ +#define _MPU6050_6AXIS_MOTIONAPPS41_H_ + +// take ownership of the "MPU6050" typedef +#define I2CDEVLIB_MPU6050_TYPEDEF + +#include "MPU6050.h" + +class MPU6050_9Axis_MotionApps41 : public MPU6050_Base { + public: + MPU6050_9Axis_MotionApps41(uint8_t address=MPU6050_DEFAULT_ADDRESS, void *wireObj=0) : MPU6050_Base(address, wireObj) { } + + uint8_t dmpInitialize(); + bool dmpPacketAvailable(); + + uint8_t dmpSetFIFORate(uint8_t fifoRate); + uint8_t dmpGetFIFORate(); + uint8_t dmpGetSampleStepSizeMS(); + uint8_t dmpGetSampleFrequency(); + int32_t dmpDecodeTemperature(int8_t tempReg); + + // Register callbacks after a packet of FIFO data is processed + //uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); + //uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func); + uint8_t dmpRunFIFORateProcesses(); + + // Setup FIFO for various output + uint8_t dmpSendQuaternion(uint_fast16_t accuracy); + uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendPacketNumber(uint_fast16_t accuracy); + uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); + uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); + + // Get Fixed Point data from FIFO + uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetMag(int16_t *data, const uint8_t* packet=0); + uint8_t dmpSetLinearAccelFilterCoefficient(float coef); + uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity); + uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q); + uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q); + uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0); + uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0); + uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0); + uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0); + + uint8_t dmpGetEuler(float *data, Quaternion *q); + uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity); + + // Get Floating Point data from FIFO + uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0); + uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0); + + uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData); + uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL); + + uint8_t dmpSetFIFOProcessedCallback(void (*func) (void)); + + uint8_t dmpInitFIFOParam(); + uint8_t dmpCloseFIFO(); + uint8_t dmpSetGyroDataSource(uint8_t source); + uint8_t dmpDecodeQuantizedAccel(); + uint32_t dmpGetGyroSumOfSquare(); + uint32_t dmpGetAccelSumOfSquare(); + void dmpOverrideQuaternion(long *q); + uint16_t dmpGetFIFOPacketSize(); + + private: + uint8_t *dmpPacketBuffer; + uint16_t dmpPacketSize; +}; + +typedef MPU6050_9Axis_MotionApps41 MPU6050; + +#endif /* _MPU6050_6AXIS_MOTIONAPPS41_H_ */ diff --git a/examples/IMU_Zero/IMU_Zero.ino b/examples/IMU_Zero/IMU_Zero.ino new file mode 100644 index 0000000..a9f46ea --- /dev/null +++ b/examples/IMU_Zero/IMU_Zero.ino @@ -0,0 +1,358 @@ +// MPU6050 offset-finder, based on Jeff Rowberg's MPU6050_RAW +// 2016-10-19 by Robert R. Fenichel (bob@fenichel.net) + +// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class +// 10/7/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2019-07-11 - added PID offset generation at begninning Generates first offsets +// - in @ 6 seconds and completes with 4 more sets @ 10 seconds +// - then continues with origional 2016 calibration code. +// 2016-11-25 - added delays to reduce sampling rate to ~200 Hz +// added temporizing printing during long computations +// 2016-10-25 - requires inequality (Low < Target, High > Target) during expansion +// dynamic speed change when closing in +// 2016-10-22 - cosmetic changes +// 2016-10-19 - initial release of IMU_Zero +// 2013-05-08 - added multiple output formats +// - added seamless Fastwire support +// 2011-10-07 - initial release of MPU6050_RAW + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2011 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + If an MPU6050 + * is an ideal member of its tribe, + * is properly warmed up, + * is at rest in a neutral position, + * is in a location where the pull of gravity is exactly 1g, and + * has been loaded with the best possible offsets, +then it will report 0 for all accelerations and displacements, except for +Z acceleration, for which it will report 16384 (that is, 2^14). Your device +probably won't do quite this well, but good offsets will all get the baseline +outputs close to these target values. + + Put the MPU6050 on a flat and horizontal surface, and leave it operating for +5-10 minutes so its temperature gets stabilized. + + Run this program. A "----- done -----" line will indicate that it has done its best. +With the current accuracy-related constants (NFast = 1000, NSlow = 10000), it will take +a few minutes to get there. + + Along the way, it will generate a dozen or so lines of output, showing that for each +of the 6 desired offsets, it is + * first, trying to find two estimates, one too low and one too high, and + * then, closing in until the bracket can't be made smaller. + + The line just above the "done" line will look something like + [567,567] --> [-1,2] [-2223,-2223] --> [0,1] [1131,1132] --> [16374,16404] [155,156] --> [-1,1] [-25,-24] --> [0,3] [5,6] --> [0,4] +As will have been shown in interspersed header lines, the six groups making up this +line describe the optimum offsets for the X acceleration, Y acceleration, Z acceleration, +X gyro, Y gyro, and Z gyro, respectively. In the sample shown just above, the trial showed +that +567 was the best offset for the X acceleration, -2223 was best for Y acceleration, +and so on. + + The need for the delay between readings (usDelay) was brought to my attention by Nikolaus Doppelhammer. +=============================================== +*/ + +// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files +// for both classes must be in the include path of your project +#include "I2Cdev.h" +#include "MPU6050.h" + +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation +// is used in I2Cdev.h +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + #include "Wire.h" +#endif + +// class default I2C address is 0x68 +// specific I2C addresses may be passed as a parameter here +// AD0 low = 0x68 (default for InvenSense evaluation board) +// AD0 high = 0x69 +MPU6050 accelgyro; +//MPU6050 accelgyro(0x69); // <-- use for AD0 high + + +const char LBRACKET = '['; +const char RBRACKET = ']'; +const char COMMA = ','; +const char BLANK = ' '; +const char PERIOD = '.'; + +const int iAx = 0; +const int iAy = 1; +const int iAz = 2; +const int iGx = 3; +const int iGy = 4; +const int iGz = 5; + +const int usDelay = 3150; // empirical, to hold sampling to 200 Hz +const int NFast = 1000; // the bigger, the better (but slower) +const int NSlow = 10000; // .. +const int LinesBetweenHeaders = 5; + int LowValue[6]; + int HighValue[6]; + int Smoothed[6]; + int LowOffset[6]; + int HighOffset[6]; + int Target[6]; + int LinesOut; + int N; + +void ForceHeader() + { LinesOut = 99; } + +void GetSmoothed() + { int16_t RawValue[6]; + int i; + long Sums[6]; + for (i = iAx; i <= iGz; i++) + { Sums[i] = 0; } +// unsigned long Start = micros(); + + for (i = 1; i <= N; i++) + { // get sums + accelgyro.getMotion6(&RawValue[iAx], &RawValue[iAy], &RawValue[iAz], + &RawValue[iGx], &RawValue[iGy], &RawValue[iGz]); + if ((i % 500) == 0) + Serial.print(PERIOD); + delayMicroseconds(usDelay); + for (int j = iAx; j <= iGz; j++) + Sums[j] = Sums[j] + RawValue[j]; + } // get sums +// unsigned long usForN = micros() - Start; +// Serial.print(" reading at "); +// Serial.print(1000000/((usForN+N/2)/N)); +// Serial.println(" Hz"); + for (i = iAx; i <= iGz; i++) + { Smoothed[i] = (Sums[i] + N/2) / N ; } + } // GetSmoothed + +void Initialize() + { + // join I2C bus (I2Cdev library doesn't do this automatically) + #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + Wire.begin(); + #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE + Fastwire::setup(400, true); + #endif + + Serial.begin(9600); + + // initialize device + Serial.println("Initializing I2C devices..."); + accelgyro.initialize(); + + // verify connection + Serial.println("Testing device connections..."); + Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed"); + Serial.println("PID tuning Each Dot = 100 readings"); + /*A tidbit on how PID (PI actually) tuning works. + When we change the offset in the MPU6050 we can get instant results. This allows us to use Proportional and + integral of the PID to discover the ideal offsets. Integral is the key to discovering these offsets, Integral + uses the error from set-point (set-point is zero), it takes a fraction of this error (error * ki) and adds it + to the integral value. Each reading narrows the error down to the desired offset. The greater the error from + set-point, the more we adjust the integral value. The proportional does its part by hiding the noise from the + integral math. The Derivative is not used because of the noise and because the sensor is stationary. With the + noise removed the integral value lands on a solid offset after just 600 readings. At the end of each set of 100 + readings, the integral value is used for the actual offsets and the last proportional reading is ignored due to + the fact it reacts to any noise. + */ + accelgyro.CalibrateAccel(6); + accelgyro.CalibrateGyro(6); + Serial.println("\nat 600 Readings"); + accelgyro.PrintActiveOffsets(); + Serial.println(); + accelgyro.CalibrateAccel(1); + accelgyro.CalibrateGyro(1); + Serial.println("700 Total Readings"); + accelgyro.PrintActiveOffsets(); + Serial.println(); + accelgyro.CalibrateAccel(1); + accelgyro.CalibrateGyro(1); + Serial.println("800 Total Readings"); + accelgyro.PrintActiveOffsets(); + Serial.println(); + accelgyro.CalibrateAccel(1); + accelgyro.CalibrateGyro(1); + Serial.println("900 Total Readings"); + accelgyro.PrintActiveOffsets(); + Serial.println(); + accelgyro.CalibrateAccel(1); + accelgyro.CalibrateGyro(1); + Serial.println("1000 Total Readings"); + accelgyro.PrintActiveOffsets(); + Serial.println("\n\n Any of the above offsets will work nice \n\n Lets proof the PID tuning using another method:"); + } // Initialize + +void SetOffsets(int TheOffsets[6]) + { accelgyro.setXAccelOffset(TheOffsets [iAx]); + accelgyro.setYAccelOffset(TheOffsets [iAy]); + accelgyro.setZAccelOffset(TheOffsets [iAz]); + accelgyro.setXGyroOffset (TheOffsets [iGx]); + accelgyro.setYGyroOffset (TheOffsets [iGy]); + accelgyro.setZGyroOffset (TheOffsets [iGz]); + } // SetOffsets + +void ShowProgress() + { if (LinesOut >= LinesBetweenHeaders) + { // show header + Serial.println("\tXAccel\t\t\tYAccel\t\t\t\tZAccel\t\t\tXGyro\t\t\tYGyro\t\t\tZGyro"); + LinesOut = 0; + } // show header + Serial.print(BLANK); + for (int i = iAx; i <= iGz; i++) + { Serial.print(LBRACKET); + Serial.print(LowOffset[i]), + Serial.print(COMMA); + Serial.print(HighOffset[i]); + Serial.print("] --> ["); + Serial.print(LowValue[i]); + Serial.print(COMMA); + Serial.print(HighValue[i]); + if (i == iGz) + { Serial.println(RBRACKET); } + else + { Serial.print("]\t"); } + } + LinesOut++; + } // ShowProgress + +void PullBracketsIn() + { boolean AllBracketsNarrow; + boolean StillWorking; + int NewOffset[6]; + + Serial.println("\nclosing in:"); + AllBracketsNarrow = false; + ForceHeader(); + StillWorking = true; + while (StillWorking) + { StillWorking = false; + if (AllBracketsNarrow && (N == NFast)) + { SetAveraging(NSlow); } + else + { AllBracketsNarrow = true; }// tentative + for (int i = iAx; i <= iGz; i++) + { if (HighOffset[i] <= (LowOffset[i]+1)) + { NewOffset[i] = LowOffset[i]; } + else + { // binary search + StillWorking = true; + NewOffset[i] = (LowOffset[i] + HighOffset[i]) / 2; + if (HighOffset[i] > (LowOffset[i] + 10)) + { AllBracketsNarrow = false; } + } // binary search + } + SetOffsets(NewOffset); + GetSmoothed(); + for (int i = iAx; i <= iGz; i++) + { // closing in + if (Smoothed[i] > Target[i]) + { // use lower half + HighOffset[i] = NewOffset[i]; + HighValue[i] = Smoothed[i]; + } // use lower half + else + { // use upper half + LowOffset[i] = NewOffset[i]; + LowValue[i] = Smoothed[i]; + } // use upper half + } // closing in + ShowProgress(); + } // still working + + } // PullBracketsIn + +void PullBracketsOut() + { boolean Done = false; + int NextLowOffset[6]; + int NextHighOffset[6]; + + Serial.println("expanding:"); + ForceHeader(); + + while (!Done) + { Done = true; + SetOffsets(LowOffset); + GetSmoothed(); + for (int i = iAx; i <= iGz; i++) + { // got low values + LowValue[i] = Smoothed[i]; + if (LowValue[i] >= Target[i]) + { Done = false; + NextLowOffset[i] = LowOffset[i] - 1000; + } + else + { NextLowOffset[i] = LowOffset[i]; } + } // got low values + + SetOffsets(HighOffset); + GetSmoothed(); + for (int i = iAx; i <= iGz; i++) + { // got high values + HighValue[i] = Smoothed[i]; + if (HighValue[i] <= Target[i]) + { Done = false; + NextHighOffset[i] = HighOffset[i] + 1000; + } + else + { NextHighOffset[i] = HighOffset[i]; } + } // got high values + ShowProgress(); + for (int i = iAx; i <= iGz; i++) + { LowOffset[i] = NextLowOffset[i]; // had to wait until ShowProgress done + HighOffset[i] = NextHighOffset[i]; // .. + } + } // keep going + } // PullBracketsOut + +void SetAveraging(int NewN) + { N = NewN; + Serial.print("averaging "); + Serial.print(N); + Serial.println(" readings each time"); + } // SetAveraging + +void setup() + { Initialize(); + for (int i = iAx; i <= iGz; i++) + { // set targets and initial guesses + Target[i] = 0; // must fix for ZAccel + HighOffset[i] = 0; + LowOffset[i] = 0; + } // set targets and initial guesses + Target[iAz] = 16384; + SetAveraging(NFast); + + PullBracketsOut(); + PullBracketsIn(); + + Serial.println("-------------- done --------------"); + } // setup + +void loop() + { + } // loop diff --git a/examples/MPU6050_DMP6/MPU6050_DMP6.ino b/examples/MPU6050_DMP6/MPU6050_DMP6.ino new file mode 100644 index 0000000..3ebe2ee --- /dev/null +++ b/examples/MPU6050_DMP6/MPU6050_DMP6.ino @@ -0,0 +1,345 @@ +// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0) +// 6/21/2012 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2019-07-08 - Added Auto Calibration and offset generator +// - and altered FIFO retrieval sequence to avoid using blocking code +// 2016-04-18 - Eliminated a potential infinite loop +// 2013-05-08 - added seamless Fastwire support +// - added note about gyro calibration +// 2012-06-21 - added note about Arduino 1.0.1 + Leonardo compatibility error +// 2012-06-20 - improved FIFO overflow handling and simplified read process +// 2012-06-19 - completely rearranged DMP initialization code and simplification +// 2012-06-13 - pull gyro and accel data from FIFO packet instead of reading directly +// 2012-06-09 - fix broken FIFO read sequence and change interrupt detection to RISING +// 2012-06-05 - add gravity-compensated initial reference frame acceleration output +// - add 3D math helper file to DMP6 example sketch +// - add Euler output and Yaw/Pitch/Roll output formats +// 2012-06-04 - remove accel offset clearing for better results (thanks Sungon Lee) +// 2012-06-01 - fixed gyro sensitivity to be 2000 deg/sec instead of 250 +// 2012-05-30 - basic DMP initialization working + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files +// for both classes must be in the include path of your project +#include "I2Cdev.h" + +#include "MPU6050_6Axis_MotionApps20.h" +//#include "MPU6050.h" // not necessary if using MotionApps include file + +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation +// is used in I2Cdev.h +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + #include "Wire.h" +#endif + +// class default I2C address is 0x68 +// specific I2C addresses may be passed as a parameter here +// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board) +// AD0 high = 0x69 +MPU6050 mpu; +//MPU6050 mpu(0x69); // <-- use for AD0 high + +/* ========================================================================= + NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch + depends on the MPU-6050's INT pin being connected to the Arduino's + external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is + digital I/O pin 2. + * ========================================================================= */ + +/* ========================================================================= + NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error + when using Serial.write(buf, len). The Teapot output uses this method. + The solution requires a modification to the Arduino USBAPI.h file, which + is fortunately simple, but annoying. This will be fixed in the next IDE + release. For more info, see these links: + + http://arduino.cc/forum/index.php/topic,109987.0.html + http://code.google.com/p/arduino/issues/detail?id=958 + * ========================================================================= */ + + + +// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual +// quaternion components in a [w, x, y, z] format (not best for parsing +// on a remote host such as Processing or something though) +//#define OUTPUT_READABLE_QUATERNION + +// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles +// (in degrees) calculated from the quaternions coming from the FIFO. +// Note that Euler angles suffer from gimbal lock (for more info, see +// http://en.wikipedia.org/wiki/Gimbal_lock) +//#define OUTPUT_READABLE_EULER + +// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/ +// pitch/roll angles (in degrees) calculated from the quaternions coming +// from the FIFO. Note this also requires gravity vector calculations. +// Also note that yaw/pitch/roll angles suffer from gimbal lock (for +// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock) +#define OUTPUT_READABLE_YAWPITCHROLL + +// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration +// components with gravity removed. This acceleration reference frame is +// not compensated for orientation, so +X is always +X according to the +// sensor, just without the effects of gravity. If you want acceleration +// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead. +//#define OUTPUT_READABLE_REALACCEL + +// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration +// components with gravity removed and adjusted for the world frame of +// reference (yaw is relative to initial orientation, since no magnetometer +// is present in this case). Could be quite handy in some cases. +//#define OUTPUT_READABLE_WORLDACCEL + +// uncomment "OUTPUT_TEAPOT" if you want output that matches the +// format used for the InvenSense teapot demo +//#define OUTPUT_TEAPOT + + + +#define INTERRUPT_PIN 2 // use pin 2 on Arduino Uno & most boards +#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6) +bool blinkState = false; + +// MPU control/status vars +bool dmpReady = false; // set true if DMP init was successful +uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU +uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) +uint16_t packetSize; // expected DMP packet size (default is 42 bytes) +uint16_t fifoCount; // count of all bytes currently in FIFO +uint8_t fifoBuffer[64]; // FIFO storage buffer + +// orientation/motion vars +Quaternion q; // [w, x, y, z] quaternion container +VectorInt16 aa; // [x, y, z] accel sensor measurements +VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements +VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements +VectorFloat gravity; // [x, y, z] gravity vector +float euler[3]; // [psi, theta, phi] Euler angle container +float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector + +// packet structure for InvenSense teapot demo +uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' }; + + + +// ================================================================ +// === INTERRUPT DETECTION ROUTINE === +// ================================================================ + +volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high +void dmpDataReady() { + mpuInterrupt = true; +} + + + +// ================================================================ +// === INITIAL SETUP === +// ================================================================ + +void setup() { + // join I2C bus (I2Cdev library doesn't do this automatically) + #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + Wire.begin(); + Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties + #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE + Fastwire::setup(400, true); + #endif + + // initialize serial communication + // (115200 chosen because it is required for Teapot Demo output, but it's + // really up to you depending on your project) + Serial.begin(115200); + while (!Serial); // wait for Leonardo enumeration, others continue immediately + + // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3V or Arduino + // Pro Mini running at 3.3V, cannot handle this baud rate reliably due to + // the baud timing being too misaligned with processor ticks. You must use + // 38400 or slower in these cases, or use some kind of external separate + // crystal solution for the UART timer. + + // initialize device + Serial.println(F("Initializing I2C devices...")); + mpu.initialize(); + pinMode(INTERRUPT_PIN, INPUT); + + // verify connection + Serial.println(F("Testing device connections...")); + Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); + + // wait for ready + Serial.println(F("\nSend any character to begin DMP programming and demo: ")); + while (Serial.available() && Serial.read()); // empty buffer + while (!Serial.available()); // wait for data + while (Serial.available() && Serial.read()); // empty buffer again + + // load and configure the DMP + Serial.println(F("Initializing DMP...")); + devStatus = mpu.dmpInitialize(); + + // supply your own gyro offsets here, scaled for min sensitivity + mpu.setXGyroOffset(220); + mpu.setYGyroOffset(76); + mpu.setZGyroOffset(-85); + mpu.setZAccelOffset(1788); // 1688 factory default for my test chip + + // make sure it worked (returns 0 if so) + if (devStatus == 0) { + // Calibration Time: generate offsets and calibrate our MPU6050 + mpu.CalibrateAccel(6); + mpu.CalibrateGyro(6); + mpu.PrintActiveOffsets(); + // turn on the DMP, now that it's ready + Serial.println(F("Enabling DMP...")); + mpu.setDMPEnabled(true); + + // enable Arduino interrupt detection + Serial.print(F("Enabling interrupt detection (Arduino external interrupt ")); + Serial.print(digitalPinToInterrupt(INTERRUPT_PIN)); + Serial.println(F(")...")); + attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); + mpuIntStatus = mpu.getIntStatus(); + + // set our DMP Ready flag so the main loop() function knows it's okay to use it + Serial.println(F("DMP ready! Waiting for first interrupt...")); + dmpReady = true; + + // get expected DMP packet size for later comparison + packetSize = mpu.dmpGetFIFOPacketSize(); + } else { + // ERROR! + // 1 = initial memory load failed + // 2 = DMP configuration updates failed + // (if it's going to break, usually the code will be 1) + Serial.print(F("DMP Initialization failed (code ")); + Serial.print(devStatus); + Serial.println(F(")")); + } + + // configure LED for output + pinMode(LED_PIN, OUTPUT); +} + + + +// ================================================================ +// === MAIN PROGRAM LOOP === +// ================================================================ + +void loop() { + // if programming failed, don't try to do anything + if (!dmpReady) return; + // read a packet from FIFO + if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet + #ifdef OUTPUT_READABLE_QUATERNION + // display quaternion values in easy matrix form: w x y z + mpu.dmpGetQuaternion(&q, fifoBuffer); + Serial.print("quat\t"); + Serial.print(q.w); + Serial.print("\t"); + Serial.print(q.x); + Serial.print("\t"); + Serial.print(q.y); + Serial.print("\t"); + Serial.println(q.z); + #endif + + #ifdef OUTPUT_READABLE_EULER + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetEuler(euler, &q); + Serial.print("euler\t"); + Serial.print(euler[0] * 180/M_PI); + Serial.print("\t"); + Serial.print(euler[1] * 180/M_PI); + Serial.print("\t"); + Serial.println(euler[2] * 180/M_PI); + #endif + + #ifdef OUTPUT_READABLE_YAWPITCHROLL + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); + Serial.print("ypr\t"); + Serial.print(ypr[0] * 180/M_PI); + Serial.print("\t"); + Serial.print(ypr[1] * 180/M_PI); + Serial.print("\t"); + Serial.println(ypr[2] * 180/M_PI); + #endif + + #ifdef OUTPUT_READABLE_REALACCEL + // display real acceleration, adjusted to remove gravity + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + Serial.print("areal\t"); + Serial.print(aaReal.x); + Serial.print("\t"); + Serial.print(aaReal.y); + Serial.print("\t"); + Serial.println(aaReal.z); + #endif + + #ifdef OUTPUT_READABLE_WORLDACCEL + // display initial world-frame acceleration, adjusted to remove gravity + // and rotated based on known orientation from quaternion + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); + Serial.print("aworld\t"); + Serial.print(aaWorld.x); + Serial.print("\t"); + Serial.print(aaWorld.y); + Serial.print("\t"); + Serial.println(aaWorld.z); + #endif + + #ifdef OUTPUT_TEAPOT + // display quaternion values in InvenSense Teapot demo format: + teapotPacket[2] = fifoBuffer[0]; + teapotPacket[3] = fifoBuffer[1]; + teapotPacket[4] = fifoBuffer[4]; + teapotPacket[5] = fifoBuffer[5]; + teapotPacket[6] = fifoBuffer[8]; + teapotPacket[7] = fifoBuffer[9]; + teapotPacket[8] = fifoBuffer[12]; + teapotPacket[9] = fifoBuffer[13]; + Serial.write(teapotPacket, 14); + teapotPacket[11]++; // packetCount, loops at 0xFF on purpose + #endif + + // blink LED to indicate activity + blinkState = !blinkState; + digitalWrite(LED_PIN, blinkState); + } +} diff --git a/examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde b/examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde new file mode 100644 index 0000000..130fc4d --- /dev/null +++ b/examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde @@ -0,0 +1,242 @@ +// I2C device class (I2Cdev) demonstration Processing sketch for MPU6050 DMP output +// 6/20/2012 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2012-06-20 - initial release + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +import processing.serial.*; +import processing.opengl.*; +import toxi.geom.*; +import toxi.processing.*; + +// NOTE: requires ToxicLibs to be installed in order to run properly. +// 1. Download from http://toxiclibs.org/downloads +// 2. Extract into [userdir]/Processing/libraries +// (location may be different on Mac/Linux) +// 3. Run and bask in awesomeness + +ToxiclibsSupport gfx; + +Serial port; // The serial port +char[] teapotPacket = new char[14]; // InvenSense Teapot packet +int serialCount = 0; // current packet byte position +int synced = 0; +int interval = 0; + +float[] q = new float[4]; +Quaternion quat = new Quaternion(1, 0, 0, 0); + +float[] gravity = new float[3]; +float[] euler = new float[3]; +float[] ypr = new float[3]; + +void setup() { + // 300px square viewport using OpenGL rendering + size(300, 300, OPENGL); + gfx = new ToxiclibsSupport(this); + + // setup lights and antialiasing + lights(); + smooth(); + + // display serial port list for debugging/clarity + println(Serial.list()); + + // get the first available port (use EITHER this OR the specific port code below) + String portName = Serial.list()[0]; + + // get a specific serial port (use EITHER this OR the first-available code above) + //String portName = "COM4"; + + // open the serial port + port = new Serial(this, portName, 115200); + + // send single character to trigger DMP init/start + // (expected by MPU6050_DMP6 example Arduino sketch) + port.write('r'); +} + +void draw() { + if (millis() - interval > 1000) { + // resend single character to trigger DMP init/start + // in case the MPU is halted/reset while applet is running + port.write('r'); + interval = millis(); + } + + // black background + background(0); + + // translate everything to the middle of the viewport + pushMatrix(); + translate(width / 2, height / 2); + + // 3-step rotation from yaw/pitch/roll angles (gimbal lock!) + // ...and other weirdness I haven't figured out yet + //rotateY(-ypr[0]); + //rotateZ(-ypr[1]); + //rotateX(-ypr[2]); + + // toxiclibs direct angle/axis rotation from quaternion (NO gimbal lock!) + // (axis order [1, 3, 2] and inversion [-1, +1, +1] is a consequence of + // different coordinate system orientation assumptions between Processing + // and InvenSense DMP) + float[] axis = quat.toAxisAngle(); + rotate(axis[0], -axis[1], axis[3], axis[2]); + + // draw main body in red + fill(255, 0, 0, 200); + box(10, 10, 200); + + // draw front-facing tip in blue + fill(0, 0, 255, 200); + pushMatrix(); + translate(0, 0, -120); + rotateX(PI/2); + drawCylinder(0, 20, 20, 8); + popMatrix(); + + // draw wings and tail fin in green + fill(0, 255, 0, 200); + beginShape(TRIANGLES); + vertex(-100, 2, 30); vertex(0, 2, -80); vertex(100, 2, 30); // wing top layer + vertex(-100, -2, 30); vertex(0, -2, -80); vertex(100, -2, 30); // wing bottom layer + vertex(-2, 0, 98); vertex(-2, -30, 98); vertex(-2, 0, 70); // tail left layer + vertex( 2, 0, 98); vertex( 2, -30, 98); vertex( 2, 0, 70); // tail right layer + endShape(); + beginShape(QUADS); + vertex(-100, 2, 30); vertex(-100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80); + vertex( 100, 2, 30); vertex( 100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80); + vertex(-100, 2, 30); vertex(-100, -2, 30); vertex(100, -2, 30); vertex(100, 2, 30); + vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, -30, 98); vertex(-2, -30, 98); + vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, 0, 70); vertex(-2, 0, 70); + vertex(-2, -30, 98); vertex(2, -30, 98); vertex(2, 0, 70); vertex(-2, 0, 70); + endShape(); + + popMatrix(); +} + +void serialEvent(Serial port) { + interval = millis(); + while (port.available() > 0) { + int ch = port.read(); + + if (synced == 0 && ch != '$') return; // initial synchronization - also used to resync/realign if needed + synced = 1; + print ((char)ch); + + if ((serialCount == 1 && ch != 2) + || (serialCount == 12 && ch != '\r') + || (serialCount == 13 && ch != '\n')) { + serialCount = 0; + synced = 0; + return; + } + + if (serialCount > 0 || ch == '$') { + teapotPacket[serialCount++] = (char)ch; + if (serialCount == 14) { + serialCount = 0; // restart packet byte position + + // get quaternion from data packet + q[0] = ((teapotPacket[2] << 8) | teapotPacket[3]) / 16384.0f; + q[1] = ((teapotPacket[4] << 8) | teapotPacket[5]) / 16384.0f; + q[2] = ((teapotPacket[6] << 8) | teapotPacket[7]) / 16384.0f; + q[3] = ((teapotPacket[8] << 8) | teapotPacket[9]) / 16384.0f; + for (int i = 0; i < 4; i++) if (q[i] >= 2) q[i] = -4 + q[i]; + + // set our toxilibs quaternion to new data + quat.set(q[0], q[1], q[2], q[3]); + + /* + // below calculations unnecessary for orientation only using toxilibs + + // calculate gravity vector + gravity[0] = 2 * (q[1]*q[3] - q[0]*q[2]); + gravity[1] = 2 * (q[0]*q[1] + q[2]*q[3]); + gravity[2] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; + + // calculate Euler angles + euler[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1); + euler[1] = -asin(2*q[1]*q[3] + 2*q[0]*q[2]); + euler[2] = atan2(2*q[2]*q[3] - 2*q[0]*q[1], 2*q[0]*q[0] + 2*q[3]*q[3] - 1); + + // calculate yaw/pitch/roll angles + ypr[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1); + ypr[1] = atan(gravity[0] / sqrt(gravity[1]*gravity[1] + gravity[2]*gravity[2])); + ypr[2] = atan(gravity[1] / sqrt(gravity[0]*gravity[0] + gravity[2]*gravity[2])); + + // output various components for debugging + //println("q:\t" + round(q[0]*100.0f)/100.0f + "\t" + round(q[1]*100.0f)/100.0f + "\t" + round(q[2]*100.0f)/100.0f + "\t" + round(q[3]*100.0f)/100.0f); + //println("euler:\t" + euler[0]*180.0f/PI + "\t" + euler[1]*180.0f/PI + "\t" + euler[2]*180.0f/PI); + //println("ypr:\t" + ypr[0]*180.0f/PI + "\t" + ypr[1]*180.0f/PI + "\t" + ypr[2]*180.0f/PI); + */ + } + } + } +} + +void drawCylinder(float topRadius, float bottomRadius, float tall, int sides) { + float angle = 0; + float angleIncrement = TWO_PI / sides; + beginShape(QUAD_STRIP); + for (int i = 0; i < sides + 1; ++i) { + vertex(topRadius*cos(angle), 0, topRadius*sin(angle)); + vertex(bottomRadius*cos(angle), tall, bottomRadius*sin(angle)); + angle += angleIncrement; + } + endShape(); + + // If it is not a cone, draw the circular top cap + if (topRadius != 0) { + angle = 0; + beginShape(TRIANGLE_FAN); + + // Center point + vertex(0, 0, 0); + for (int i = 0; i < sides + 1; i++) { + vertex(topRadius * cos(angle), 0, topRadius * sin(angle)); + angle += angleIncrement; + } + endShape(); + } + + // If it is not a cone, draw the circular bottom cap + if (bottomRadius != 0) { + angle = 0; + beginShape(TRIANGLE_FAN); + + // Center point + vertex(0, tall, 0); + for (int i = 0; i < sides + 1; i++) { + vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle)); + angle += angleIncrement; + } + endShape(); + } +} diff --git a/examples/MPU6050_DMP6_ESPWiFi/MPU6050_DMP6_ESPWiFi.ino b/examples/MPU6050_DMP6_ESPWiFi/MPU6050_DMP6_ESPWiFi.ino new file mode 100644 index 0000000..a080869 --- /dev/null +++ b/examples/MPU6050_DMP6_ESPWiFi/MPU6050_DMP6_ESPWiFi.ino @@ -0,0 +1,373 @@ +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +/* This driver reads quaternion data from the MPU6060 and sends + Open Sound Control messages. + + GY-521 NodeMCU + MPU6050 devkit 1.0 + board Lolin Description + ======= ========== ==================================================== + VCC VU (5V USB) Not available on all boards so use 3.3V if needed. + GND G Ground + SCL D1 (GPIO05) I2C clock + SDA D2 (GPIO04) I2C data + XDA not connected + XCL not connected + AD0 not connected + INT D8 (GPIO15) Interrupt pin + +*/ + +#if defined(ESP8266) +#include +#else +#include +#endif +#include +#include +#include +#include +#include //https://github.com/tzapu/WiFiManager + +// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files +// for both classes must be in the include path of your project +#include "I2Cdev.h" + +#include "MPU6050_6Axis_MotionApps20.h" +//#include "MPU6050.h" // not necessary if using MotionApps include file + +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation +// is used in I2Cdev.h +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + #include "Wire.h" +#endif + +// class default I2C address is 0x68 +// specific I2C addresses may be passed as a parameter here +// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board) +// AD0 high = 0x69 +MPU6050 mpu; +//MPU6050 mpu(0x69); // <-- use for AD0 high + +/* ========================================================================= + NOTE: In addition to connection 5/3.3v, GND, SDA, and SCL, this sketch + depends on the MPU-6050's INT pin being connected to the ESP8266 GPIO15 + pin. + * ========================================================================= */ + +// MPU control/status vars +bool dmpReady = false; // set true if DMP init was successful +uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU +uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) +uint16_t packetSize; // expected DMP packet size (default is 42 bytes) +uint16_t fifoCount; // count of all bytes currently in FIFO +uint8_t fifoBuffer[64]; // FIFO storage buffer + +// orientation/motion vars +Quaternion q; // [w, x, y, z] quaternion container +VectorInt16 aa; // [x, y, z] accel sensor measurements +VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements +VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements +VectorFloat gravity; // [x, y, z] gravity vector + + +// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual +// quaternion components in a [w, x, y, z] format (not best for parsing +// on a remote host such as Processing or something though) +//#define OUTPUT_READABLE_QUATERNION + +// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles +// (in degrees) calculated from the quaternions coming from the FIFO. +// Note that Euler angles suffer from gimbal lock (for more info, see +// http://en.wikipedia.org/wiki/Gimbal_lock) +//#define OUTPUT_READABLE_EULER + +// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/ +// pitch/roll angles (in degrees) calculated from the quaternions coming +// from the FIFO. Note this also requires gravity vector calculations. +// Also note that yaw/pitch/roll angles suffer from gimbal lock (for +// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock) +//#define OUTPUT_READABLE_YAWPITCHROLL + +// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration +// components with gravity removed. This acceleration reference frame is +// not compensated for orientation, so +X is always +X according to the +// sensor, just without the effects of gravity. If you want acceleration +// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead. +//#define OUTPUT_READABLE_REALACCEL + +// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration +// components with gravity removed and adjusted for the world frame of +// reference (yaw is relative to initial orientation, since no magnetometer +// is present in this case). Could be quite handy in some cases. +//#define OUTPUT_READABLE_WORLDACCEL + +// uncomment "OUTPUT_TEAPOT_OSC" if you want output that matches the +// format used for the InvenSense teapot demo +#define OUTPUT_TEAPOT_OSC + + +#ifdef OUTPUT_READABLE_EULER +float euler[3]; // [psi, theta, phi] Euler angle container +#endif +#ifdef OUTPUT_READABLE_YAWPITCHROLL +float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector +#endif + +#define INTERRUPT_PIN 15 // use pin 15 on ESP8266 + +const char DEVICE_NAME[] = "mpu6050"; + +WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP +const IPAddress outIp(192, 168, 1, 11); // remote IP to receive OSC +const unsigned int outPort = 9999; // remote port to receive OSC + +// ================================================================ +// === INTERRUPT DETECTION ROUTINE === +// ================================================================ + +volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high +void ICACHE_RAM_ATTR dmpDataReady() { + mpuInterrupt = true; +} + +void mpu_setup() +{ + // join I2C bus (I2Cdev library doesn't do this automatically) +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + Wire.begin(); + Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties +#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE + Fastwire::setup(400, true); +#endif + + // initialize device + Serial.println(F("Initializing I2C devices...")); + mpu.initialize(); + pinMode(INTERRUPT_PIN, INPUT); + + // verify connection + Serial.println(F("Testing device connections...")); + Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); + + // load and configure the DMP + Serial.println(F("Initializing DMP...")); + devStatus = mpu.dmpInitialize(); + + // supply your own gyro offsets here, scaled for min sensitivity + mpu.setXGyroOffset(220); + mpu.setYGyroOffset(76); + mpu.setZGyroOffset(-85); + mpu.setZAccelOffset(1788); // 1688 factory default for my test chip + + // make sure it worked (returns 0 if so) + if (devStatus == 0) { + // turn on the DMP, now that it's ready + Serial.println(F("Enabling DMP...")); + mpu.setDMPEnabled(true); + + // enable Arduino interrupt detection + Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)...")); + attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); + mpuIntStatus = mpu.getIntStatus(); + + // set our DMP Ready flag so the main loop() function knows it's okay to use it + Serial.println(F("DMP ready! Waiting for first interrupt...")); + dmpReady = true; + + // get expected DMP packet size for later comparison + packetSize = mpu.dmpGetFIFOPacketSize(); + } else { + // ERROR! + // 1 = initial memory load failed + // 2 = DMP configuration updates failed + // (if it's going to break, usually the code will be 1) + Serial.print(F("DMP Initialization failed (code ")); + Serial.print(devStatus); + Serial.println(F(")")); + } +} + +void setup(void) +{ + Serial.begin(115200); + Serial.println(F("\nOrientation Sensor OSC output")); Serial.println(); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + WiFiManager wifiManager; + //reset saved settings + //wifiManager.resetSettings(); + + //fetches ssid and pass from eeprom and tries to connect + //if it does not connect it starts an access point with the specified name + //and goes into a blocking loop awaiting configuration + wifiManager.autoConnect(DEVICE_NAME); + + Serial.print(F("WiFi connected! IP address: ")); + Serial.println(WiFi.localIP()); + + mpu_setup(); +} + +void mpu_loop() +{ + // if programming failed, don't try to do anything + if (!dmpReady) return; + + // wait for MPU interrupt or extra packet(s) available + if (!mpuInterrupt && fifoCount < packetSize) return; + + // reset interrupt flag and get INT_STATUS byte + mpuInterrupt = false; + mpuIntStatus = mpu.getIntStatus(); + + // get current FIFO count + fifoCount = mpu.getFIFOCount(); + + // check for overflow (this should never happen unless our code is too inefficient) + if ((mpuIntStatus & 0x10) || fifoCount == 1024) { + // reset so we can continue cleanly + mpu.resetFIFO(); + Serial.println(F("FIFO overflow!")); + + // otherwise, check for DMP data ready interrupt (this should happen frequently) + } else if (mpuIntStatus & 0x02) { + // wait for correct available data length, should be a VERY short wait + while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); + + // read a packet from FIFO + mpu.getFIFOBytes(fifoBuffer, packetSize); + + // track FIFO count here in case there is > 1 packet available + // (this lets us immediately read more without waiting for an interrupt) + fifoCount -= packetSize; + +#ifdef OUTPUT_READABLE_QUATERNION + // display quaternion values in easy matrix form: w x y z + mpu.dmpGetQuaternion(&q, fifoBuffer); + Serial.print("quat\t"); + Serial.print(q.w); + Serial.print("\t"); + Serial.print(q.x); + Serial.print("\t"); + Serial.print(q.y); + Serial.print("\t"); + Serial.println(q.z); +#endif + +#ifdef OUTPUT_TEAPOT_OSC +#ifndef OUTPUT_READABLE_QUATERNION + // display quaternion values in easy matrix form: w x y z + mpu.dmpGetQuaternion(&q, fifoBuffer); +#endif + // Send OSC message + OSCMessage msg("/imuquat"); + msg.add((float)q.w); + msg.add((float)q.x); + msg.add((float)q.y); + msg.add((float)q.z); + + Udp.beginPacket(outIp, outPort); + msg.send(Udp); + Udp.endPacket(); + + msg.empty(); +#endif + +#ifdef OUTPUT_READABLE_EULER + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetEuler(euler, &q); + Serial.print("euler\t"); + Serial.print(euler[0] * 180/M_PI); + Serial.print("\t"); + Serial.print(euler[1] * 180/M_PI); + Serial.print("\t"); + Serial.println(euler[2] * 180/M_PI); +#endif + +#ifdef OUTPUT_READABLE_YAWPITCHROLL + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); + Serial.print("ypr\t"); + Serial.print(ypr[0] * 180/M_PI); + Serial.print("\t"); + Serial.print(ypr[1] * 180/M_PI); + Serial.print("\t"); + Serial.println(ypr[2] * 180/M_PI); +#endif + +#ifdef OUTPUT_READABLE_REALACCEL + // display real acceleration, adjusted to remove gravity + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + Serial.print("areal\t"); + Serial.print(aaReal.x); + Serial.print("\t"); + Serial.print(aaReal.y); + Serial.print("\t"); + Serial.println(aaReal.z); +#endif + +#ifdef OUTPUT_READABLE_WORLDACCEL + // display initial world-frame acceleration, adjusted to remove gravity + // and rotated based on known orientation from quaternion + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); + Serial.print("aworld\t"); + Serial.print(aaWorld.x); + Serial.print("\t"); + Serial.print(aaWorld.y); + Serial.print("\t"); + Serial.println(aaWorld.z); +#endif + } +} + +/**************************************************************************/ +/* + Arduino loop function, called once 'setup' is complete (your own code + should go here) +*/ +/**************************************************************************/ +void loop(void) +{ + if (WiFi.status() != WL_CONNECTED) { + Serial.println(); + Serial.println("*** Disconnected from AP so rebooting ***"); + Serial.println(); + ESP.reset(); + } + + mpu_loop(); +} diff --git a/examples/MPU6050_DMP6_ESPWiFi/Processing/MPUOSCTeapot/MPUOSCTeapot.pde b/examples/MPU6050_DMP6_ESPWiFi/Processing/MPUOSCTeapot/MPUOSCTeapot.pde new file mode 100644 index 0000000..dfe6518 --- /dev/null +++ b/examples/MPU6050_DMP6_ESPWiFi/Processing/MPUOSCTeapot/MPUOSCTeapot.pde @@ -0,0 +1,188 @@ +// I2C device class (I2Cdev) demonstration Processing sketch for MPU6050 DMP output +// 6/20/2012 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2012-06-20 - initial release + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +/** + * MPUOSCTeapot Processing demo for MPU6050 DMP modified for OSC + * https://gitub.com/jrowberg/i2cdevlib + * The original demo uses serial port I/O which has been replaced with + * OSC UDP messages in this sketch. + * + * The MPU6050 is connected to an ESP8266 with battery so it is completely + * wire free. + * + * Tested on Processing 3.3.5 running on Ubuntu Linux 14.04 + * + * Dependencies installed using Library Manager + * + * Open Sound Control library + * oscP5 website at http://www.sojamo.de/oscP5 + * ToxicLibs + * quaternion functions http://toxiclibs.org/ + * + */ + +// Install oscP5 using the IDE library manager. +// From the IDE menu bar, Sketch | Import Library | Add library. +// In the search box type "osc". +import oscP5.*; +import netP5.*; +// Install ToxicLibs using the IDE library manager +// From the IDE menu bar, Sketch | Import Library | Add library. +// In the search box type "toxic". +import toxi.geom.*; +import toxi.processing.*; + +ToxiclibsSupport gfx; + +Quaternion quat = new Quaternion(1, 0, 0, 0); + +OscP5 oscP5; + +void setup() { + // 300px square viewport using OpenGL rendering + size(300, 300, P3D); + gfx = new ToxiclibsSupport(this); + + // setup lights and antialiasing + lights(); + smooth(); + + /* start oscP5, listening for incoming messages at port 9999 */ + oscP5 = new OscP5(this, 9999); + + oscP5.plug(this, "imu", "/imuquat"); +} + +/* incoming osc message are forwarded to the oscEvent method. */ +void oscEvent(OscMessage theOscMessage) { + /* print the address pattern and the typetag of the received OscMessage */ + //print("### received an osc message."); + //print(" addrpattern: "+theOscMessage.addrPattern()); + //println(" typetag: "+theOscMessage.typetag()); +} + +public void imu(float quant_w, float quant_x, float quant_y, float quant_z) { + //println(quant_w, quant_x, quant_y, quant_z); + quat.set(quant_w, quant_x, quant_y, quant_z); +} + +void draw() { + // black background + background(0); + + // translate everything to the middle of the viewport + pushMatrix(); + translate(width / 2, height / 2); + + // 3-step rotation from yaw/pitch/roll angles (gimbal lock!) + // ...and other weirdness I haven't figured out yet + //rotateY(-ypr[0]); + //rotateZ(-ypr[1]); + //rotateX(-ypr[2]); + + // toxiclibs direct angle/axis rotation from quaternion (NO gimbal lock!) + // (axis order [1, 3, 2] and inversion [-1, +1, +1] is a consequence of + // different coordinate system orientation assumptions between Processing + // and InvenSense DMP) + float[] axis = quat.toAxisAngle(); + rotate(axis[0], -axis[1], axis[3], axis[2]); + + // draw main body in red + fill(255, 0, 0, 200); + box(10, 10, 200); + + // draw front-facing tip in blue + fill(0, 0, 255, 200); + pushMatrix(); + translate(0, 0, -120); + rotateX(PI/2); + drawCylinder(0, 20, 20, 8); + popMatrix(); + + // draw wings and tail fin in green + fill(0, 255, 0, 200); + beginShape(TRIANGLES); + vertex(-100, 2, 30); vertex(0, 2, -80); vertex(100, 2, 30); // wing top layer + vertex(-100, -2, 30); vertex(0, -2, -80); vertex(100, -2, 30); // wing bottom layer + vertex(-2, 0, 98); vertex(-2, -30, 98); vertex(-2, 0, 70); // tail left layer + vertex( 2, 0, 98); vertex( 2, -30, 98); vertex( 2, 0, 70); // tail right layer + endShape(); + beginShape(QUADS); + vertex(-100, 2, 30); vertex(-100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80); + vertex( 100, 2, 30); vertex( 100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80); + vertex(-100, 2, 30); vertex(-100, -2, 30); vertex(100, -2, 30); vertex(100, 2, 30); + vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, -30, 98); vertex(-2, -30, 98); + vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, 0, 70); vertex(-2, 0, 70); + vertex(-2, -30, 98); vertex(2, -30, 98); vertex(2, 0, 70); vertex(-2, 0, 70); + endShape(); + + popMatrix(); +} + +void drawCylinder(float topRadius, float bottomRadius, float tall, int sides) { + float angle = 0; + float angleIncrement = TWO_PI / sides; + beginShape(QUAD_STRIP); + for (int i = 0; i < sides + 1; ++i) { + vertex(topRadius*cos(angle), 0, topRadius*sin(angle)); + vertex(bottomRadius*cos(angle), tall, bottomRadius*sin(angle)); + angle += angleIncrement; + } + endShape(); + + // If it is not a cone, draw the circular top cap + if (topRadius != 0) { + angle = 0; + beginShape(TRIANGLE_FAN); + + // Center point + vertex(0, 0, 0); + for (int i = 0; i < sides + 1; i++) { + vertex(topRadius * cos(angle), 0, topRadius * sin(angle)); + angle += angleIncrement; + } + endShape(); + } + + // If it is not a cone, draw the circular bottom cap + if (bottomRadius != 0) { + angle = 0; + beginShape(TRIANGLE_FAN); + + // Center point + vertex(0, tall, 0); + for (int i = 0; i < sides + 1; i++) { + vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle)); + angle += angleIncrement; + } + endShape(); + } +} diff --git a/examples/MPU6050_DMP6_Ethernet/MPU6050_DMP6_Ethernet.ino b/examples/MPU6050_DMP6_Ethernet/MPU6050_DMP6_Ethernet.ino new file mode 100644 index 0000000..b2e2081 --- /dev/null +++ b/examples/MPU6050_DMP6_Ethernet/MPU6050_DMP6_Ethernet.ino @@ -0,0 +1,545 @@ +// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0) over Ethernet +// 2/27/2016 by hellphoenix +// Based on I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0) (6/21/2012 by Jeff Rowberg ) +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2016-04-18 - Eliminated a potential infinite loop +// 2016-02-28 - Cleaned up code to be in line with other example codes +// - Added Ethernet outputs for Quaternion, Euler, RealAccel, WorldAccel +// 2016-02-27 - Initial working code compiled +// Bugs: +// - There is still a hangup after some time, though it only occurs when you are reading data from the website. +// If you only read the data from the serial port, there are no hangups. +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ +#include +// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files +// for both classes must be in the include path of your project +#include "I2Cdev.h" + +#include "MPU6050_6Axis_MotionApps20.h" +//#include "MPU6050.h" // not necessary if using MotionApps include file + +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation +// is used in I2Cdev.h +#include "Wire.h" +#include "avr/wdt.h"// Watchdog library + +// class default I2C address is 0x68 +// specific I2C addresses may be passed as a parameter here +// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board) +// AD0 high = 0x69 +MPU6050 mpu; +//MPU6050 mpu(0x69); // <-- use for AD0 high + +// MAC address from Ethernet shield sticker under board +byte mac[] = { + 0x90, 0xA2, 0xDA, 0x10, 0x26, 0x82 +}; +// assign an IP address for the controller: +IPAddress ip(192,168,1,50); +// the router's gateway address: +byte gateway[] = { 192, 168, 1, 1 }; +// the subnet: +byte subnet[] = { 255, 255, 0, 0 }; + +// Initialize the Ethernet server library +// with the IP address and port you want to use +// (port 80 is default for HTTP): +EthernetServer server(80); + +String HTTP_req; // stores the HTTP request + +/* ========================================================================= + NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch + depends on the MPU-6050's INT pin being connected to the Arduino's + external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is + digital I/O pin 2. + * ========================================================================= */ + +/* ========================================================================= + NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error + when using Serial.write(buf, len). The Teapot output uses this method. + The solution requires a modification to the Arduino USBAPI.h file, which + is fortunately simple, but annoying. This will be fixed in the next IDE + release. For more info, see these links: + + http://arduino.cc/forum/index.php/topic,109987.0.html + http://code.google.com/p/arduino/issues/detail?id=958 + * ========================================================================= */ + + +// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual +// quaternion components in a [w, x, y, z] format (not best for parsing +// on a remote host such as Processing or something though) +//#define OUTPUT_READABLE_QUATERNION + +// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles +// (in degrees) calculated from the quaternions coming from the FIFO. +// Note that Euler angles suffer from gimbal lock (for more info, see +// http://en.wikipedia.org/wiki/Gimbal_lock) +//#define OUTPUT_READABLE_EULER + +// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/ +// pitch/roll angles (in degrees) calculated from the quaternions coming +// from the FIFO. Note this also requires gravity vector calculations. +// Also note that yaw/pitch/roll angles suffer from gimbal lock (for +// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock) +#define OUTPUT_READABLE_YAWPITCHROLL + +// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration +// components with gravity removed. This acceleration reference frame is +// not compensated for orientation, so +X is always +X according to the +// sensor, just without the effects of gravity. If you want acceleration +// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead. +//#define OUTPUT_READABLE_REALACCEL + +// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration +// components with gravity removed and adjusted for the world frame of +// reference (yaw is relative to initial orientation, since no magnetometer +// is present in this case). Could be quite handy in some cases. +//#define OUTPUT_READABLE_WORLDACCEL + +// uncomment "OUTPUT_TEAPOT" if you want output that matches the +// format used for the InvenSense teapot demo +//#define OUTPUT_TEAPOT + +#define INTERRUPT_PIN 2 // use pin 2 on Arduino Uno & most boards +#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6) +bool blinkState = false; + +// MPU control/status vars +bool dmpReady = false; // set true if DMP init was successful +uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU +uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) +uint16_t packetSize; // expected DMP packet size (default is 42 bytes) +uint16_t fifoCount; // count of all bytes currently in FIFO +uint8_t fifoBuffer[64]; // FIFO storage buffer + +// orientation/motion vars +Quaternion q; // [w, x, y, z] quaternion container +VectorInt16 aa; // [x, y, z] accel sensor measurements +VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements +VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements +VectorFloat gravity; // [x, y, z] gravity vector +float euler[3]; // [psi, theta, phi] Euler angle container +float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector + +// packet structure for InvenSense teapot demo +uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' }; + + + +// ================================================================ +// === INTERRUPT DETECTION ROUTINE === +// ================================================================ + +volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high +void dmpDataReady() { + mpuInterrupt = true; +} +// ================================================================ +// === INITIAL SETUP === +// ================================================================ + +void setup() { + wdt_enable(WDTO_1S); //Watchdog enable. + //WDTO_1S sets the watchdog timer to 1 second. The time set here is approximate. + // You can find more time settings at http://www.nongnu.org/avr-libc/user-manual/group__avr__watchdog.html . + + // join I2C bus (I2Cdev library doesn't do this automatically) + #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + Wire.begin(); + Wire.setClock(400000); // 400kHz I2C clock (200kHz if CPU is 8MHz). Comment this line if having compilation difficulties + #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE + Fastwire::setup(400, true); + #endif + + // initialize serial communication + // (115200 chosen because it is required for Teapot Demo output, but it's + // really up to you depending on your project) + Serial.begin(115200); + // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3V or Arduino + // Pro Mini running at 3.3V, cannot handle this baud rate reliably due to + // the baud timing being too misaligned with processor ticks. You must use + // 38400 or slower in these cases, or use some kind of external separate + // crystal solution for the UART timer. + + Ethernet.begin(mac, ip, gateway, subnet); + server.begin(); + Serial.print("server is at "); + Serial.println(Ethernet.localIP()); + while (!Serial); // wait for Leonardo enumeration, others continue immediately + + // initialize device + Serial.println(F("Initializing I2C devices...")); + mpu.initialize(); + pinMode(INTERRUPT_PIN, INPUT); + + // verify connection + Serial.println(F("Testing device connections...")); + Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); + + // load and configure the DMP + Serial.println(F("Initializing DMP...")); + devStatus = mpu.dmpInitialize(); + + // supply your own gyro offsets here, scaled for min sensitivity + mpu.setXGyroOffset(220); + mpu.setYGyroOffset(76); + mpu.setZGyroOffset(-85); + mpu.setZAccelOffset(1788); // 1688 factory default for my test chip + + // make sure it worked (returns 0 if so) + if (devStatus == 0) { + // turn on the DMP, now that it's ready + Serial.println(F("Enabling DMP...")); + mpu.setDMPEnabled(true); + + // enable Arduino interrupt detection + Serial.print(F("Enabling interrupt detection (Arduino external interrupt ")); + Serial.print(digitalPinToInterrupt(INTERRUPT_PIN)); + Serial.println(F(")...")); + attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); + mpuIntStatus = mpu.getIntStatus(); + + // set our DMP Ready flag so the main loop() function knows it's okay to use it + Serial.println(F("DMP ready! Waiting for first interrupt...")); + dmpReady = true; + + // get expected DMP packet size for later comparison + packetSize = mpu.dmpGetFIFOPacketSize(); + } else { + // ERROR! + // 1 = initial memory load failed + // 2 = DMP configuration updates failed + // (if it's going to break, usually the code will be 1) + Serial.print(F("DMP Initialization failed (code ")); + Serial.print(devStatus); + Serial.println(F(")")); + } + + // configure LED for output + pinMode(LED_PIN, OUTPUT); +} + + + +// ================================================================ +// === MAIN PROGRAM LOOP === +// ================================================================ + +void loop() { + // if programming failed, don't try to do anything + if (!dmpReady) return; + + wdt_reset();//Resets the watchdog timer. If the timer is not reset, and the timer expires, a watchdog-initiated device reset will occur. + // wait for MPU interrupt or extra packet(s) available + while (!mpuInterrupt && fifoCount < packetSize) { + if (mpuInterrupt && fifoCount < packetSize) { + // try to get out of the infinite loop + fifoCount = mpu.getFIFOCount(); + } + // other program behavior stuff here + // . + // . + // if you are really paranoid you can frequently test in between other + // stuff to see if mpuInterrupt is true, and if so, "break;" from the + // while() loop to immediately process the MPU data + // . + // . + // . + } + + // reset interrupt flag and get INT_STATUS byte + mpuInterrupt = false; + mpuIntStatus = mpu.getIntStatus(); + + // get current FIFO count + fifoCount = mpu.getFIFOCount(); + + // check for overflow (this should never happen unless our code is too inefficient) + if ((mpuIntStatus & (1 << MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) { + // reset so we can continue cleanly + mpu.resetFIFO(); + fifoCount = mpu.getFIFOCount(); + Serial.println(F("FIFO overflow!")); + + // otherwise, check for DMP data ready interrupt (this should happen frequently) + } else if (mpuIntStatus & (1 << MPU6050_INTERRUPT_DMP_INT_BIT)) { + // wait for correct available data length, should be a VERY short wait + while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); + + // read a packet from FIFO, then clear the buffer + mpu.getFIFOBytes(fifoBuffer, packetSize); + //mpu.resetFIFO(); + + // track FIFO count here in case there is > 1 packet available + // (this lets us immediately read more without waiting for an interrupt) + fifoCount -= packetSize; + + #ifdef OUTPUT_READABLE_QUATERNION + // display quaternion values in easy matrix form: w x y z + mpu.dmpGetQuaternion(&q, fifoBuffer); + Serial.print("quat\t"); + Serial.print(q.w); + Serial.print("\t"); + Serial.print(q.x); + Serial.print("\t"); + Serial.print(q.y); + Serial.print("\t"); + Serial.println(q.z); + #endif + #ifdef OUTPUT_READABLE_EULER + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetEuler(euler, &q); + Serial.print("euler\t"); + Serial.print(euler[0] * 180/M_PI); + Serial.print("\t"); + Serial.print(euler[1] * 180/M_PI); + Serial.print("\t"); + Serial.println(euler[2] * 180/M_PI); + #endif + #ifdef OUTPUT_READABLE_YAWPITCHROLL + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); + Serial.print("ypr\t"); + Serial.print(ypr[0] * 180/M_PI); + Serial.print("\t"); + Serial.print(ypr[1] * 180/M_PI); + Serial.print("\t"); + Serial.println(ypr[2] * 180/M_PI); + #endif + #ifdef OUTPUT_READABLE_REALACCEL + // display real acceleration, adjusted to remove gravity + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + Serial.print("areal\t"); + Serial.print(aaReal.x); + Serial.print("\t"); + Serial.print(aaReal.y); + Serial.print("\t"); + Serial.println(aaReal.z); + #endif + #ifdef OUTPUT_READABLE_WORLDACCEL + // display initial world-frame acceleration, adjusted to remove gravity + // and rotated based on known orientation from quaternion + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); + Serial.print("aworld\t"); + Serial.print(aaWorld.x); + Serial.print("\t"); + Serial.print(aaWorld.y); + Serial.print("\t"); + Serial.println(aaWorld.z); + #endif + #ifdef OUTPUT_TEAPOT + // display quaternion values in InvenSense Teapot demo format: + teapotPacket[2] = fifoBuffer[0]; + teapotPacket[3] = fifoBuffer[1]; + teapotPacket[4] = fifoBuffer[4]; + teapotPacket[5] = fifoBuffer[5]; + teapotPacket[6] = fifoBuffer[8]; + teapotPacket[7] = fifoBuffer[9]; + teapotPacket[8] = fifoBuffer[12]; + teapotPacket[9] = fifoBuffer[13]; + Serial.write(teapotPacket, 14); + teapotPacket[11]++; // packetCount, loops at 0xFF on purpose + #endif + serversend(); + // blink LED to indicate activity + blinkState = !blinkState; + digitalWrite(LED_PIN, blinkState); + } +} + +void serversend(){ + + EthernetClient client = server.available(); // try to get client + + if (client) { // got client? + //boolean currentLineIsBlank = true; + while (client.connected()) { + if (client.available()) { // client data available to read + char c = client.read(); // read 1 byte (character) from client + HTTP_req += c; // save the HTTP request 1 char at a time + // last line of client request is blank and ends with \n + // respond to client only after last line received + if (c == '\n') { + // send a standard http response header + client.println("HTTP/1.1 200 OK"); + client.println("Content-Type: text/html"); + //client.println("Connection: keep-alive"); + client.println(); + // AJAX request for switch state + if (HTTP_req.indexOf("ajax_switch") > -1) { + // read switch state and analog input + GetAjaxData(client); + } + else { // HTTP request for web page + // send web page - contains JavaScript with AJAX calls + client.println(""); + client.println(""); + client.println(""); + client.println("Arduino Web Page"); + client.println(""); + client.println(""); + client.println(""); + client.println("

MPU6050 Output

"); + client.println("
"); + client.println("
"); + client.println(""); + client.println(""); + } + // display received HTTP request on serial port + Serial.print(HTTP_req); + HTTP_req = ""; // finished with request, empty string + client.stop(); // close the connection + break; + } + } + } + } +} + +void GetAjaxData(EthernetClient cl) +{ + #ifdef OUTPUT_READABLE_QUATERNION + // display quaternion values in easy matrix form: w x y z + cl.print("Quaternion Values:\t"); + cl.print("

w:"); + cl.print(q.w); + cl.print("\t"); + cl.println("

"); + cl.print("

x:"); + cl.print(q.x); + cl.print("\t"); + cl.println("

"); + cl.print("

y:"); + cl.print(q.y); + cl.print("\t"); + cl.println("

"); + cl.print("

z:"); + cl.print(q.z); + cl.print("\t"); + cl.println("

"); + #endif + #ifdef OUTPUT_READABLE_EULER + // display Euler angles in degrees + cl.print("Euler Angles:\t"); + cl.print("

Yaw:"); + cl.print(euler[0] * 180/M_PI); + cl.print("\t"); + cl.println("

"); + cl.print("

Pitch:"); + cl.print(euler[2] * 180/M_PI); + cl.print("\t"); + cl.println("

"); + cl.print("

Roll:"); + cl.print(euler[1] * 180/M_PI); + cl.print("\t"); + cl.println("

"); + #endif + #ifdef OUTPUT_READABLE_YAWPITCHROLL + // display Yaw/Pitch/Roll values in degrees + cl.print("Yaw, Pitch, and Roll:\t"); + cl.print("

Yaw:"); + cl.print(ypr[0] * 180/M_PI); + cl.print("\t"); + cl.println("

"); + cl.print("

Pitch:"); + cl.print(ypr[2] * 180/M_PI); + cl.print("\t"); + cl.println("

"); + cl.print("

Roll:"); + cl.print(ypr[1] * 180/M_PI); + cl.print("\t"); + cl.println("

"); + #endif + #ifdef OUTPUT_READABLE_REALACCEL + // display real acceleration, adjusted to remove gravity + cl.print("Real Accel:\t"); + cl.print("

Yaw:"); + cl.print(aaReal.x); + cl.print("\t"); + cl.println("

"); + cl.print("

Pitch:"); + cl.print(aaReal.z); + cl.print("\t"); + cl.println("

"); + cl.print("

Roll:"); + cl.print(aaReal.y); + cl.print("\t"); + cl.println("

"); + #endif + #ifdef OUTPUT_READABLE_WORLDACCEL + // display initial world-frame acceleration, adjusted to remove gravity + // and rotated based on known orientation from quaternion + cl.print("World Accel:\t"); + cl.print("

Yaw:"); + cl.print(aaWorld.x); + cl.print("\t"); + cl.println("

"); + cl.print("

Pitch:"); + cl.print(aaWorld.z); + cl.print("\t"); + cl.println("

"); + cl.print("

Roll:"); + cl.print(aaWorld.y); + cl.print("\t"); + cl.println("

"); + #endif + #ifdef OUTPUT_TEAPOT + cl.print("

teapotpacket:"); + cl.write(teapotPacket, 14); + cl.print("\t"); + cl.println("

"); + #endif +} diff --git a/examples/MPU6050_DMP6_using_DMP_V6.12/MPU6050_DMP6_using_DMP_V6.12.ino b/examples/MPU6050_DMP6_using_DMP_V6.12/MPU6050_DMP6_using_DMP_V6.12.ino new file mode 100644 index 0000000..fd7ebf9 --- /dev/null +++ b/examples/MPU6050_DMP6_using_DMP_V6.12/MPU6050_DMP6_using_DMP_V6.12.ino @@ -0,0 +1,368 @@ +// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v6.12) +// 6/21/2012 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2019-07-10 - Uses the new version of the DMP Firmware V6.12 +// - Note: I believe the Teapot demo is broken with this versin as +// - the fifo buffer structure has changed +// 2016-04-18 - Eliminated a potential infinite loop +// 2013-05-08 - added seamless Fastwire support +// - added note about gyro calibration +// 2012-06-21 - added note about Arduino 1.0.1 + Leonardo compatibility error +// 2012-06-20 - improved FIFO overflow handling and simplified read process +// 2012-06-19 - completely rearranged DMP initialization code and simplification +// 2012-06-13 - pull gyro and accel data from FIFO packet instead of reading directly +// 2012-06-09 - fix broken FIFO read sequence and change interrupt detection to RISING +// 2012-06-05 - add gravity-compensated initial reference frame acceleration output +// - add 3D math helper file to DMP6 example sketch +// - add Euler output and Yaw/Pitch/Roll output formats +// 2012-06-04 - remove accel offset clearing for better results (thanks Sungon Lee) +// 2012-06-01 - fixed gyro sensitivity to be 2000 deg/sec instead of 250 +// 2012-05-30 - basic DMP initialization working + +/* ============================================ + I2Cdev device library code is placed under the MIT license + Copyright (c) 2012 Jeff Rowberg + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + =============================================== +*/ + +// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files +// for both classes must be in the include path of your project +#include "I2Cdev.h" + +#include "MPU6050_6Axis_MotionApps612.h" +//#include "MPU6050.h" // not necessary if using MotionApps include file + +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation +// is used in I2Cdev.h +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE +#include "Wire.h" +#endif + +// class default I2C address is 0x68 +// specific I2C addresses may be passed as a parameter here +// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board) +// AD0 high = 0x69 +MPU6050 mpu; +//MPU6050 mpu(0x69); // <-- use for AD0 high + +/* ========================================================================= + NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch + depends on the MPU-6050's INT pin being connected to the Arduino's + external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is + digital I/O pin 2. + ========================================================================= */ + +/* ========================================================================= + NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error + when using Serial.write(buf, len). The Teapot output uses this method. + The solution requires a modification to the Arduino USBAPI.h file, which + is fortunately simple, but annoying. This will be fixed in the next IDE + release. For more info, see these links: + + http://arduino.cc/forum/index.php/topic,109987.0.html + http://code.google.com/p/arduino/issues/detail?id=958 + ========================================================================= */ + + + +// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual +// quaternion components in a [w, x, y, z] format (not best for parsing +// on a remote host such as Processing or something though) +//#define OUTPUT_READABLE_QUATERNION + +// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles +// (in degrees) calculated from the quaternions coming from the FIFO. +// Note that Euler angles suffer from gimbal lock (for more info, see +// http://en.wikipedia.org/wiki/Gimbal_lock) +//#define OUTPUT_READABLE_EULER + +// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/ +// pitch/roll angles (in degrees) calculated from the quaternions coming +// from the FIFO. Note this also requires gravity vector calculations. +// Also note that yaw/pitch/roll angles suffer from gimbal lock (for +// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock) +#define OUTPUT_READABLE_YAWPITCHROLL + +// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration +// components with gravity removed. This acceleration reference frame is +// not compensated for orientation, so +X is always +X according to the +// sensor, just without the effects of gravity. If you want acceleration +// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead. +//#define OUTPUT_READABLE_REALACCEL + +// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration +// components with gravity removed and adjusted for the world frame of +// reference (yaw is relative to initial orientation, since no magnetometer +// is present in this case). Could be quite handy in some cases. +//#define OUTPUT_READABLE_WORLDACCEL + +// uncomment "OUTPUT_TEAPOT" if you want output that matches the +// format used for the InvenSense teapot demo +//#define OUTPUT_TEAPOT + + + +#define INTERRUPT_PIN 2 // use pin 2 on Arduino Uno & most boards +#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6) +bool blinkState = false; + +// MPU control/status vars +bool dmpReady = false; // set true if DMP init was successful +uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU +uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) +uint16_t packetSize; // expected DMP packet size (default is 42 bytes) +uint16_t fifoCount; // count of all bytes currently in FIFO +uint8_t fifoBuffer[64]; // FIFO storage buffer + +// orientation/motion vars +Quaternion q; // [w, x, y, z] quaternion container +VectorInt16 aa; // [x, y, z] accel sensor measurements +VectorInt16 gy; // [x, y, z] gyro sensor measurements +VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements +VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements +VectorFloat gravity; // [x, y, z] gravity vector +float euler[3]; // [psi, theta, phi] Euler angle container +float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector + +// packet structure for InvenSense teapot demo +uint8_t teapotPacket[14] = { '$', 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, '\r', '\n' }; + + + +// ================================================================ +// === INTERRUPT DETECTION ROUTINE === +// ================================================================ + +volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high +void dmpDataReady() { + mpuInterrupt = true; +} + + + +// ================================================================ +// === INITIAL SETUP === +// ================================================================ + +void setup() { + // join I2C bus (I2Cdev library doesn't do this automatically) +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + Wire.begin(); + Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties +#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE + Fastwire::setup(400, true); +#endif + + // initialize serial communication + // (115200 chosen because it is required for Teapot Demo output, but it's + // really up to you depending on your project) + Serial.begin(115200); + while (!Serial); // wait for Leonardo enumeration, others continue immediately + + // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3V or Arduino + // Pro Mini running at 3.3V, cannot handle this baud rate reliably due to + // the baud timing being too misaligned with processor ticks. You must use + // 38400 or slower in these cases, or use some kind of external separate + // crystal solution for the UART timer. + + // initialize device + Serial.println(F("Initializing I2C devices...")); + mpu.initialize(); + pinMode(INTERRUPT_PIN, INPUT); + + // verify connection + Serial.println(F("Testing device connections...")); + Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); + + // wait for ready + Serial.println(F("\nSend any character to begin DMP programming and demo: ")); + while (Serial.available() && Serial.read()); // empty buffer + while (!Serial.available()); // wait for data + while (Serial.available() && Serial.read()); // empty buffer again + + // load and configure the DMP + Serial.println(F("Initializing DMP...")); + devStatus = mpu.dmpInitialize(); + + // supply your own gyro offsets here, scaled for min sensitivity + mpu.setXGyroOffset(51); + mpu.setYGyroOffset(8); + mpu.setZGyroOffset(21); + mpu.setXAccelOffset(1150); + mpu.setYAccelOffset(-50); + mpu.setZAccelOffset(1060); + // make sure it worked (returns 0 if so) + if (devStatus == 0) { + // Calibration Time: generate offsets and calibrate our MPU6050 + mpu.CalibrateAccel(6); + mpu.CalibrateGyro(6); + Serial.println(); + mpu.PrintActiveOffsets(); + // turn on the DMP, now that it's ready + Serial.println(F("Enabling DMP...")); + mpu.setDMPEnabled(true); + + // enable Arduino interrupt detection + Serial.print(F("Enabling interrupt detection (Arduino external interrupt ")); + Serial.print(digitalPinToInterrupt(INTERRUPT_PIN)); + Serial.println(F(")...")); + attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); + mpuIntStatus = mpu.getIntStatus(); + + // set our DMP Ready flag so the main loop() function knows it's okay to use it + Serial.println(F("DMP ready! Waiting for first interrupt...")); + dmpReady = true; + + // get expected DMP packet size for later comparison + packetSize = mpu.dmpGetFIFOPacketSize(); + } else { + // ERROR! + // 1 = initial memory load failed + // 2 = DMP configuration updates failed + // (if it's going to break, usually the code will be 1) + Serial.print(F("DMP Initialization failed (code ")); + Serial.print(devStatus); + Serial.println(F(")")); + } + + // configure LED for output + pinMode(LED_PIN, OUTPUT); +} + + + +// ================================================================ +// === MAIN PROGRAM LOOP === +// ================================================================ + +void loop() { + // if programming failed, don't try to do anything + if (!dmpReady) return; + // read a packet from FIFO + if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet + +#ifdef OUTPUT_READABLE_QUATERNION + // display quaternion values in easy matrix form: w x y z + mpu.dmpGetQuaternion(&q, fifoBuffer); + Serial.print("quat\t"); + Serial.print(q.w); + Serial.print("\t"); + Serial.print(q.x); + Serial.print("\t"); + Serial.print(q.y); + Serial.print("\t"); + Serial.println(q.z); +#endif + +#ifdef OUTPUT_READABLE_EULER + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetEuler(euler, &q); + Serial.print("euler\t"); + Serial.print(euler[0] * 180 / M_PI); + Serial.print("\t"); + Serial.print(euler[1] * 180 / M_PI); + Serial.print("\t"); + Serial.println(euler[2] * 180 / M_PI); +#endif + +#ifdef OUTPUT_READABLE_YAWPITCHROLL + // display Euler angles in degrees + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); + Serial.print("ypr\t"); + Serial.print(ypr[0] * 180 / M_PI); + Serial.print("\t"); + Serial.print(ypr[1] * 180 / M_PI); + Serial.print("\t"); + Serial.print(ypr[2] * 180 / M_PI); + /* + mpu.dmpGetAccel(&aa, fifoBuffer); + Serial.print("\tRaw Accl XYZ\t"); + Serial.print(aa.x); + Serial.print("\t"); + Serial.print(aa.y); + Serial.print("\t"); + Serial.print(aa.z); + mpu.dmpGetGyro(&gy, fifoBuffer); + Serial.print("\tRaw Gyro XYZ\t"); + Serial.print(gy.x); + Serial.print("\t"); + Serial.print(gy.y); + Serial.print("\t"); + Serial.print(gy.z); + */ + Serial.println(); + +#endif + +#ifdef OUTPUT_READABLE_REALACCEL + // display real acceleration, adjusted to remove gravity + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + Serial.print("areal\t"); + Serial.print(aaReal.x); + Serial.print("\t"); + Serial.print(aaReal.y); + Serial.print("\t"); + Serial.println(aaReal.z); +#endif + +#ifdef OUTPUT_READABLE_WORLDACCEL + // display initial world-frame acceleration, adjusted to remove gravity + // and rotated based on known orientation from quaternion + mpu.dmpGetQuaternion(&q, fifoBuffer); + mpu.dmpGetAccel(&aa, fifoBuffer); + mpu.dmpGetGravity(&gravity, &q); + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); + mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); + Serial.print("aworld\t"); + Serial.print(aaWorld.x); + Serial.print("\t"); + Serial.print(aaWorld.y); + Serial.print("\t"); + Serial.println(aaWorld.z); +#endif + +#ifdef OUTPUT_TEAPOT + // display quaternion values in InvenSense Teapot demo format: + teapotPacket[2] = fifoBuffer[0]; + teapotPacket[3] = fifoBuffer[1]; + teapotPacket[4] = fifoBuffer[4]; + teapotPacket[5] = fifoBuffer[5]; + teapotPacket[6] = fifoBuffer[8]; + teapotPacket[7] = fifoBuffer[9]; + teapotPacket[8] = fifoBuffer[12]; + teapotPacket[9] = fifoBuffer[13]; + Serial.write(teapotPacket, 14); + teapotPacket[11]++; // packetCount, loops at 0xFF on purpose +#endif + + // blink LED to indicate activity + blinkState = !blinkState; + digitalWrite(LED_PIN, blinkState); + } +} diff --git a/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/MPUplane.pde b/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/MPUplane.pde new file mode 100644 index 0000000..ce4a6fd --- /dev/null +++ b/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/MPUplane.pde @@ -0,0 +1,189 @@ +// I2C device class (I2Cdev) demonstration Processing sketch for MPU6050 DMP output +// 6/20/2012 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2012-06-20 - initial release +// 2016-10-28 - Changed to bi-plane 3d model based on tutorial at +// https://forum.processing.org/two/discussion/24350/display-obj-file-in-3d +// https://opengameart.org/content/low-poly-biplane + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +import processing.serial.*; +//import processing.opengl.*; +import toxi.geom.*; +import toxi.processing.*; + +// NOTE: requires ToxicLibs to be installed in order to run properly. +// 1. Download from http://toxiclibs.org/downloads +// 2. Extract into [userdir]/Processing/libraries +// (location may be different on Mac/Linux) +// 3. Run and bask in awesomeness + +ToxiclibsSupport gfx; + +Serial port; // The serial port +char[] teapotPacket = new char[14]; // InvenSense Teapot packet +int serialCount = 0; // current packet byte position +int synced = 0; +int interval = 0; + +float[] q = new float[4]; +Quaternion quat = new Quaternion(1, 0, 0, 0); + +float[] gravity = new float[3]; +float[] euler = new float[3]; +float[] ypr = new float[3]; + + +PShape plane; // 3d model + +void setup() { + // 640x480 px square viewport + size(640, 480, P3D); + gfx = new ToxiclibsSupport(this); + + // setup lights and antialiasing + lights(); + smooth(); + + // display serial port list for debugging/clarity + println(Serial.list()); + + // get a specific serial port + String portName = "COM12"; + + // open the serial port + port = new Serial(this, portName, 115200); + + // send single character to trigger DMP init/start + // (expected by MPU6050_DMP6 example Arduino sketch) + port.write('r'); + + // Load Plane object + // The file must be in the \data folder + // of the current sketch to load successfully + plane = loadShape("biplane.obj"); + + + // apply its texture and set orientation + PImage img1=loadImage("diffuse_512.png"); + plane.setTexture(img1); + plane.scale(30); + plane.rotateX(PI); + plane.rotateY(PI+HALF_PI); + + +} + +void draw() { + if (millis() - interval > 1000) { + // resend single character to trigger DMP init/start + // in case the MPU is halted/reset while applet is running + port.write('r'); + interval = millis(); + } + + // black background + background(0); + + + // translate everything to the middle of the viewport + pushMatrix(); + translate(width / 2, height / 2); + + // toxiclibs direct angle/axis rotation from quaternion (NO gimbal lock!) + // (axis order [1, 3, 2] and inversion [-1, +1, +1] is a consequence of + // different coordinate system orientation assumptions between Processing + // and InvenSense DMP) + float[] axis = quat.toAxisAngle(); + rotate(axis[0], -axis[1], axis[3], axis[2]); + + // draw plane + shape(plane, 0, 0); + + popMatrix(); +} + +void serialEvent(Serial port) { + interval = millis(); + while (port.available() > 0) { + int ch = port.read(); + + if (synced == 0 && ch != '$') return; // initial synchronization - also used to resync/realign if needed + synced = 1; + print ((char)ch); + + if ((serialCount == 1 && ch != 2) + || (serialCount == 12 && ch != '\r') + || (serialCount == 13 && ch != '\n')) { + serialCount = 0; + synced = 0; + return; + } + + if (serialCount > 0 || ch == '$') { + teapotPacket[serialCount++] = (char)ch; + if (serialCount == 14) { + serialCount = 0; // restart packet byte position + + // get quaternion from data packet + q[0] = ((teapotPacket[2] << 8) | teapotPacket[3]) / 16384.0f; + q[1] = ((teapotPacket[4] << 8) | teapotPacket[5]) / 16384.0f; + q[2] = ((teapotPacket[6] << 8) | teapotPacket[7]) / 16384.0f; + q[3] = ((teapotPacket[8] << 8) | teapotPacket[9]) / 16384.0f; + for (int i = 0; i < 4; i++) if (q[i] >= 2) q[i] = -4 + q[i]; + + // set our toxilibs quaternion to new data + quat.set(q[0], q[1], q[2], q[3]); + + + // below calculations unnecessary for orientation only using toxilibs + + // calculate gravity vector + gravity[0] = 2 * (q[1]*q[3] - q[0]*q[2]); + gravity[1] = 2 * (q[0]*q[1] + q[2]*q[3]); + gravity[2] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; + + // calculate Euler angles + euler[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1); + euler[1] = -asin(2*q[1]*q[3] + 2*q[0]*q[2]); + euler[2] = atan2(2*q[2]*q[3] - 2*q[0]*q[1], 2*q[0]*q[0] + 2*q[3]*q[3] - 1); + + // calculate yaw/pitch/roll angles + ypr[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1); + ypr[1] = atan(gravity[0] / sqrt(gravity[1]*gravity[1] + gravity[2]*gravity[2])); + ypr[2] = atan(gravity[1] / sqrt(gravity[0]*gravity[0] + gravity[2]*gravity[2])); + + // output various components for debugging + println("q:\t" + round(q[0]*100.0f)/100.0f + "\t" + round(q[1]*100.0f)/100.0f + "\t" + round(q[2]*100.0f)/100.0f + "\t" + round(q[3]*100.0f)/100.0f); + println("euler:\t" + euler[0]*180.0f/PI + "\t" + euler[1]*180.0f/PI + "\t" + euler[2]*180.0f/PI); + println("ypr:\t" + ypr[0]*180.0f/PI + "\t" + ypr[1]*180.0f/PI + "\t" + ypr[2]*180.0f/PI); + + } + } + } +} diff --git a/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/data/biplane.obj b/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/data/biplane.obj new file mode 100644 index 0000000..6867928 --- /dev/null +++ b/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/data/biplane.obj @@ -0,0 +1,2239 @@ +v 4.500000 0.176989 0.367522 +v 4.500000 0.318923 0.254332 +v 4.500000 0.397690 0.090770 +v 4.500000 0.397690 -0.090770 +v 4.500000 0.318923 -0.254333 +v 4.500000 0.176989 -0.367521 +v 4.500000 0.000000 -0.407918 +v 4.500000 -0.176989 -0.367521 +v 4.500000 -0.318923 -0.254332 +v 4.500000 -0.397691 -0.090770 +v 4.500000 -0.397691 0.090770 +v 4.500000 -0.318923 0.254333 +v 4.500001 -0.176989 0.367522 +v 4.500000 0.000000 0.407918 +v 2.700000 0.259478 0.538812 +v 2.700000 0.467563 0.372869 +v 2.700000 0.583042 0.133076 +v 2.700000 0.583042 -0.133076 +v 2.700000 0.467563 -0.372869 +v 2.700000 0.259478 -0.538812 +v 2.700000 0.000000 -0.598036 +v 2.700000 -0.259478 -0.538812 +v 2.700000 -0.467563 -0.372869 +v 2.700000 -0.583042 -0.133075 +v 2.700000 -0.583042 0.133075 +v 2.700000 -0.467563 0.372870 +v 2.700000 -0.259478 0.538812 +v 2.700000 0.000000 0.598036 +v 0.900000 0.323112 0.670950 +v 0.900000 0.582229 0.464312 +v 0.900000 0.726027 0.165711 +v 0.900000 0.726027 -0.165711 +v 0.900000 0.582229 -0.464312 +v 0.900000 0.323113 -0.670950 +v 0.900000 0.000000 -0.744698 +v 0.900000 -0.323112 -0.670950 +v 0.900000 -0.582229 -0.464312 +v 0.900000 -0.726027 -0.165711 +v 0.900000 -0.726027 0.165711 +v 0.900000 -0.582229 0.464312 +v 0.900000 -0.323113 0.670950 +v 0.900000 0.000000 0.744699 +v -0.900000 0.384390 0.798195 +v -0.900000 0.692647 0.552368 +v -0.475444 0.863717 0.197138 +v -0.475445 0.863717 -0.197138 +v -0.900000 0.692647 -0.552368 +v -0.900000 0.384390 -0.798194 +v -0.900000 0.000000 -0.885929 +v -0.900000 -0.384390 -0.798195 +v -0.900000 -0.692647 -0.552368 +v -0.900000 -0.863717 -0.197138 +v -0.900000 -0.863717 0.197138 +v -0.900000 -0.692647 0.552368 +v -0.900000 -0.384390 0.798195 +v -0.900000 0.000000 0.885929 +v -2.700000 0.433883 0.900969 +v -2.700000 0.781831 0.623490 +v -2.885778 0.974928 0.222521 +v -2.885778 0.974928 -0.222521 +v -2.700000 0.781832 -0.623490 +v -2.699999 0.433884 -0.900969 +v -2.699999 0.000000 -1.000000 +v -2.700000 -0.433883 -0.900969 +v -2.700000 -0.781831 -0.623490 +v -2.700000 -0.974928 -0.222521 +v -2.700000 -0.974928 0.222521 +v -2.700000 -0.781832 0.623489 +v -2.700000 -0.433884 0.900969 +v -2.700000 0.000000 1.000000 +v 4.500000 0.000000 -0.000000 +v -5.181942 0.000000 -0.000000 +v 5.269916 1.092397 0.090771 +v 5.269916 1.092397 -0.090770 +v 3.399458 1.972455 0.000001 +v 5.551387 0.000000 -0.000000 +v 4.141554 0.176989 1.784806 +v 4.500000 0.318923 1.235124 +v 2.700000 0.467563 1.342699 +v 3.058446 0.259478 1.802116 +v 4.500000 0.318923 -1.235123 +v 4.141554 0.176989 -1.784806 +v 3.058446 0.259478 -1.802116 +v 2.700000 0.467563 -1.342699 +v -1.300000 1.103771 0.487962 +v -1.125000 1.252653 0.195352 +v -2.475000 1.333997 0.213918 +v -2.300000 1.156989 0.519851 +v -1.125000 1.252653 -0.195351 +v -2.475000 1.333997 -0.213917 +v -1.300000 1.103772 -0.487962 +v -2.300000 1.156989 -0.519851 +v -1.799999 1.281724 0.398879 +v -1.125000 1.187470 0.380784 +v -1.800000 1.350005 0.204635 +v -2.475000 1.262618 0.416974 +v -1.800000 1.156763 0.540666 +v -1.800000 1.372766 0.000000 +v -1.125000 1.274381 0.000000 +v -1.800000 1.350006 -0.204634 +v -2.475000 1.357790 0.000000 +v -1.800000 1.281724 -0.398879 +v -1.125000 1.187470 -0.380784 +v -1.800000 1.156763 -0.540666 +v -2.475000 1.262618 -0.416974 +v -0.825593 1.004904 0.374753 +v -0.475445 0.778182 0.374753 +v -0.825593 1.069055 0.192257 +v -1.125000 0.951901 0.516854 +v -2.700000 1.105101 0.423005 +v -2.885779 0.878380 0.423006 +v -2.475000 1.021542 0.564478 +v -2.700000 1.177512 0.217012 +v -1.800000 0.963961 0.587929 +v -1.800000 0.737239 0.587929 +v -0.825593 1.090439 0.000000 +v -0.475445 0.863717 0.000000 +v -0.825593 1.069055 -0.192257 +v -2.700000 1.201650 0.000000 +v -2.885779 0.974928 0.000000 +v -2.700000 1.177513 -0.217012 +v -0.825592 1.004904 -0.374753 +v -0.475444 0.778182 -0.374753 +v -1.125000 0.951901 -0.516854 +v -1.800000 0.963961 -0.587929 +v -1.800000 0.737239 -0.587929 +v -2.475000 1.021542 -0.564478 +v -2.700000 1.105101 -0.423005 +v -2.885778 0.878380 -0.423005 +v 2.700000 0.583042 0.000000 +v 0.900000 0.726027 0.005801 +v -5.181943 -0.629401 -0.501931 +v -4.986976 -0.781831 -0.623490 +v -5.181942 -0.784850 -0.179137 +v -4.986976 -0.974928 -0.222521 +v -5.181942 -0.784850 0.179137 +v -4.986976 -0.974928 0.222521 +v -5.181942 -0.629401 0.501930 +v -4.986977 -0.781831 0.623490 +v -5.181942 -0.349291 0.725311 +v -4.986976 -0.433884 0.900969 +v -4.986976 0.000000 1.000000 +v -5.181942 0.000000 0.805034 +v -5.181942 0.629401 -0.501930 +v -4.987885 0.789991 -0.606546 +v -4.986976 0.781832 -0.623490 +v -5.181942 0.349291 -0.725310 +v -4.986977 0.433884 -0.900968 +v -4.986976 0.433884 0.900969 +v -5.181942 0.349291 0.725311 +v -4.986976 0.781831 0.623490 +v -4.987885 0.789991 0.606546 +v -5.181942 0.629401 0.501931 +v -4.986976 0.974928 0.222521 +v -5.181942 0.784301 0.179012 +v -5.181942 0.000000 -0.805034 +v -4.986976 0.000000 -1.000000 +v -5.181942 -0.349291 -0.725310 +v -4.986976 -0.433884 -0.900969 +v -5.181942 0.784301 -0.179011 +v -4.986976 0.974928 -0.222521 +v -5.181942 0.779962 0.000001 +v -4.986976 0.974928 0.000000 +vt 0.455704 0.153054 +vt 0.419776 0.128644 +vt 0.383319 0.143119 +vt 0.328896 0.143119 +vt 0.292439 0.128644 +vt 0.256511 0.153054 +vt 0.245563 0.201018 +vt 0.256511 0.248981 +vt 0.287184 0.287445 +vt 0.331509 0.308790 +vt 0.380706 0.308790 +vt 0.425031 0.287445 +vt 0.455704 0.248981 +vt 0.466652 0.201018 +vt 0.005173 0.359445 +vt 0.016095 0.354912 +vt 0.061664 0.354281 +vt 0.166522 0.327467 +vt 0.186034 0.323730 +vt 0.219371 0.310411 +vt 0.248725 0.289652 +vt 0.263886 0.273262 +vt 0.275993 0.321494 +vt 0.305445 0.321494 +vt 0.334898 0.321494 +vt 0.364351 0.321494 +vt 0.393803 0.321494 +vt 0.423256 0.321494 +vt 0.452709 0.321494 +vt 0.019054 0.449768 +vt 0.082140 0.442189 +vt 0.099276 0.445505 +vt 0.128729 0.445505 +vt 0.158182 0.445505 +vt 0.187634 0.445505 +vt 0.217087 0.445505 +vt 0.246540 0.445505 +vt 0.275993 0.445505 +vt 0.305445 0.445505 +vt 0.334898 0.445505 +vt 0.364351 0.445505 +vt 0.393803 0.445505 +vt 0.423256 0.445505 +vt 0.452709 0.445505 +vt 0.040371 0.569517 +vt 0.069824 0.569517 +vt 0.099276 0.569517 +vt 0.128729 0.569517 +vt 0.158182 0.569517 +vt 0.187634 0.569517 +vt 0.217087 0.569517 +vt 0.246540 0.569517 +vt 0.275993 0.569517 +vt 0.305445 0.569517 +vt 0.334898 0.569517 +vt 0.364351 0.569517 +vt 0.393803 0.569517 +vt 0.423256 0.569517 +vt 0.452709 0.569517 +vt 0.040371 0.693528 +vt 0.073505 0.709029 +vt 0.099276 0.685777 +vt 0.128729 0.685777 +vt 0.154500 0.709029 +vt 0.187634 0.693528 +vt 0.217087 0.693528 +vt 0.246540 0.693528 +vt 0.275993 0.693528 +vt 0.305445 0.693528 +vt 0.334898 0.693528 +vt 0.364351 0.693528 +vt 0.393803 0.693528 +vt 0.423256 0.693528 +vt 0.452709 0.693528 +vt 0.040371 0.817539 +vt 0.073505 0.802038 +vt 0.154500 0.802038 +vt 0.356107 0.180046 +vt 0.098756 0.319100 +vt 0.114003 0.445505 +vt 0.087917 0.323202 +vt 0.360587 0.109025 +vt 0.356108 0.136215 +vt 0.351628 0.109025 +vt 0.023534 0.441375 +vt 0.044323 0.425256 +vt 0.082140 0.442189 +vt 0.019054 0.449768 +vt 0.204695 0.353513 +vt 0.203181 0.378966 +vt 0.187634 0.445505 +vt 0.158182 0.445505 +vt 0.076369 0.721086 +vt 0.099276 0.709029 +vt 0.099276 0.802038 +vt 0.076369 0.789982 +vt 0.128729 0.709029 +vt 0.128729 0.802038 +vt 0.151637 0.721086 +vt 0.151637 0.789982 +vt 0.084550 0.755534 +vt 0.084550 0.709029 +vt 0.099276 0.755534 +vt 0.084550 0.802038 +vt 0.073505 0.755534 +vt 0.114003 0.755534 +vt 0.114003 0.709029 +vt 0.128729 0.755534 +vt 0.114003 0.802038 +vt 0.143455 0.755534 +vt 0.143455 0.709029 +vt 0.154500 0.755534 +vt 0.143455 0.802038 +vt 0.084550 0.693528 +vt 0.084550 0.693528 +vt 0.099276 0.693528 +vt 0.073505 0.709029 +vt 0.084550 0.817539 +vt 0.073505 0.802038 +vt 0.099276 0.817539 +vt 0.069824 0.755534 +vt 0.069824 0.755534 +vt 0.114003 0.693528 +vt 0.114003 0.693528 +vt 0.128729 0.693528 +vt 0.114003 0.817539 +vt 0.128729 0.817539 +vt 0.143455 0.693528 +vt 0.143455 0.693528 +vt 0.154500 0.709029 +vt 0.158182 0.755534 +vt 0.158182 0.755534 +vt 0.154500 0.802038 +vt 0.143455 0.817539 +vt 0.114003 0.445505 +vt 0.113487 0.569517 +vt 0.084550 0.817539 +vt 0.063450 0.938935 +vt 0.275993 0.817539 +vt 0.060733 0.120401 +vt 0.305445 0.817539 +vt 0.104650 0.099252 +vt 0.334898 0.817539 +vt 0.153395 0.099252 +vt 0.364351 0.817539 +vt 0.197313 0.120401 +vt 0.423256 0.925429 +vt 0.393803 0.817539 +vt 0.423256 0.817539 +vt 0.143455 0.817539 +vt 0.060733 0.291667 +vt 0.030341 0.253557 +vt 0.449688 0.935863 +vt 0.452709 0.817539 +vt 0.053984 0.934786 +vt 0.017895 0.932919 +vt 0.100219 0.950069 +vt 0.102123 0.931740 +vt 0.099276 0.825290 +vt 0.217087 0.817539 +vt 0.187634 0.817539 +vt 0.019494 0.206034 +vt 0.030341 0.158511 +vt 0.246540 0.817539 +vt 0.104667 0.312738 +vt 0.129023 0.201653 +vt 0.128729 0.825290 +vt 0.114003 0.817539 +vt 0.129023 0.312124 +vt 0.275993 0.931809 +vt 0.273121 0.941551 +vt 0.249411 0.941551 +vt 0.278864 0.941551 +vt 0.305445 0.931809 +vt 0.302574 0.941551 +vt 0.308316 0.941551 +vt 0.334898 0.931809 +vt 0.332027 0.941551 +vt 0.337769 0.941551 +vt 0.364351 0.931809 +vt 0.361480 0.941551 +vt 0.367222 0.941551 +vt 0.396310 0.932910 +vt 0.390932 0.941551 +vt 0.227704 0.158511 +vt 0.153378 0.312738 +vt 0.066951 0.949302 +vt 0.411698 0.969294 +vt 0.007495 0.983733 +vt 0.156937 0.931070 +vt 0.155311 0.941551 +vt 0.131609 0.941551 +vt 0.161053 0.941551 +vt 0.187634 0.931809 +vt 0.184763 0.941551 +vt 0.190506 0.941551 +vt 0.217087 0.931809 +vt 0.214216 0.941551 +vt 0.437979 0.994491 +vt 0.197313 0.291667 +vt 0.050289 0.993403 +vt 0.393883 0.992961 +vt 0.238551 0.206034 +vt 0.102156 0.941551 +vt 0.114003 0.931021 +vt 0.219958 0.941551 +vt 0.246540 0.931809 +vt 0.243669 0.941551 +vt 0.128729 0.931680 +vt 0.125850 0.941551 +vt 0.114003 0.941551 +vt 0.158299 0.939937 +vt 0.157893 0.930592 +vt 0.227704 0.253557 +vt 0.046355 0.992034 +vt 0.421247 0.969294 +vn 0.089573 0.940981 0.326392 +vn 0.089573 0.940981 0.326392 +vn 0.089573 0.940981 0.326392 +vn 0.089573 0.940981 0.326392 +vn 0.089574 0.940981 -0.326392 +vn 0.089574 0.940981 -0.326392 +vn 0.089574 0.940981 -0.326392 +vn 0.089574 0.940981 -0.326392 +vn 0.150361 -0.955640 -0.253265 +vn 0.180233 0.000001 -0.983624 +vn 0.091281 0.000000 -0.995825 +vn -0.150994 -0.690915 -0.706992 +vn 0.180233 -0.426779 -0.886214 +vn 0.091281 -0.432073 -0.897208 +vn 0.180233 -0.769028 -0.613280 +vn 0.091281 -0.778567 -0.620887 +vn 0.180233 -0.958962 -0.218877 +vn 0.091281 -0.970858 -0.221593 +vn 0.180233 -0.958962 0.218877 +vn 0.091281 -0.970858 0.221592 +vn 0.180233 -0.769028 0.613280 +vn 0.091281 -0.778567 0.620887 +vn 0.180234 -0.426778 0.886214 +vn 0.091281 -0.432072 0.897208 +vn 0.180233 -0.000000 0.983624 +vn 0.091281 0.000000 0.995825 +vn 0.150361 -0.955640 0.253265 +vn 0.016625 -0.653805 0.756481 +vn 0.079554 0.000000 -0.996831 +vn 0.079554 0.432509 -0.898113 +vn 0.079554 -0.432508 -0.898113 +vn 0.079554 -0.779353 -0.621514 +vn 0.079554 -0.971838 -0.221816 +vn 0.079554 -0.971838 0.221816 +vn 0.079554 -0.779354 0.621514 +vn 0.079554 -0.432509 0.898113 +vn 0.079554 0.000000 0.996831 +vn 0.070121 -0.432816 -0.898751 +vn 0.070121 0.000000 -0.997539 +vn 0.070121 -0.779907 -0.621955 +vn 0.070121 -0.972528 -0.221973 +vn 0.070121 -0.972528 0.221973 +vn 0.070121 -0.779907 0.621955 +vn 0.070121 -0.432816 0.898751 +vn 0.130833 0.712633 0.689229 +vn 0.130833 0.712633 0.689229 +vn 0.130833 0.712633 0.689229 +vn 0.130833 0.712633 0.689229 +vn 0.143365 0.983605 0.109401 +vn 0.143365 0.983605 0.109401 +vn 0.143365 0.983605 0.109401 +vn 0.143365 0.983605 0.109401 +vn 0.139254 0.934218 -0.328397 +vn 0.139254 0.934218 -0.328397 +vn 0.139254 0.934218 -0.328397 +vn 0.139254 0.934218 -0.328397 +vn 0.026848 -0.433727 -0.900644 +vn 0.026848 0.000000 -0.999640 +vn 0.026848 -0.781550 -0.623265 +vn 0.026848 -0.974576 -0.222441 +vn 0.026848 -0.974577 0.222441 +vn 0.026848 -0.781550 0.623265 +vn 0.026848 -0.433728 0.900644 +vn -0.064587 -0.972892 -0.222057 +vn -0.064587 -0.780199 -0.622188 +vn -0.064587 -0.972892 0.222056 +vn -0.064587 -0.780199 0.622188 +vn -0.064587 -0.432978 0.899088 +vn 0.870350 -0.485974 0.079502 +vn 1.000000 -0.000000 -0.000000 +vn 1.000000 0.000002 -0.000003 +vn 0.870349 -0.485975 -0.079503 +vn 1.000000 -0.000003 0.000000 +vn 0.733799 -0.670454 0.109683 +vn 0.733799 -0.670454 0.109683 +vn 0.082297 0.996608 0.000000 +vn 0.082297 0.996608 0.000000 +vn 0.082297 0.996608 0.000000 +vn 0.082297 0.996608 0.000000 +vn -0.842065 -0.522167 0.135162 +vn -0.842065 -0.522167 0.135162 +vn -0.842065 -0.522167 0.135162 +vn -0.842065 -0.522167 0.135162 +vn -0.057129 -0.998367 0.000000 +vn -0.057129 -0.998367 0.000000 +vn 0.733799 -0.670455 -0.109683 +vn 0.733799 -0.670455 -0.109683 +vn -0.057129 -0.998367 -0.000000 +vn -0.057129 -0.998367 -0.000000 +vn -0.045744 0.737399 -0.673906 +vn -0.842065 -0.522167 -0.135162 +vn -0.842065 -0.522167 -0.135162 +vn 0.082297 0.996608 0.000000 +vn 0.082297 0.996608 0.000000 +vn 0.082297 0.996608 0.000000 +vn 0.082297 0.996608 0.000000 +vn 0.139254 0.934218 0.328398 +vn 0.139254 0.934218 0.328398 +vn 0.139254 0.934218 0.328398 +vn 0.139254 0.934218 0.328398 +vn -0.017811 0.943260 0.331576 +vn -0.017811 0.943260 0.331576 +vn -0.017811 0.943260 0.331576 +vn -0.017811 0.943260 0.331576 +vn -0.013215 0.722929 0.690796 +vn -0.013215 0.722929 0.690796 +vn -0.013215 0.722929 0.690796 +vn -0.013215 0.722929 0.690796 +vn 0.143365 0.983605 -0.109401 +vn 0.143365 0.983605 -0.109401 +vn 0.143365 0.983605 -0.109401 +vn 0.143365 0.983605 -0.109401 +vn -0.022046 0.993630 -0.110516 +vn -0.022046 0.993630 -0.110516 +vn -0.022046 0.993630 -0.110516 +vn -0.022046 0.993630 -0.110516 +vn -0.022046 0.993630 0.110516 +vn -0.022046 0.993630 0.110516 +vn -0.022046 0.993630 0.110516 +vn -0.022046 0.993630 0.110516 +vn 0.130833 0.712634 -0.689229 +vn 0.130833 0.712634 -0.689229 +vn 0.130833 0.712634 -0.689229 +vn 0.130833 0.712634 -0.689229 +vn -0.013215 0.722930 -0.690795 +vn -0.013215 0.722930 -0.690795 +vn -0.013215 0.722930 -0.690795 +vn -0.013215 0.722930 -0.690795 +vn -0.017811 0.943260 -0.331576 +vn -0.017811 0.943260 -0.331576 +vn -0.017811 0.943260 -0.331576 +vn -0.017811 0.943260 -0.331576 +vn -0.545911 0.790429 -0.277854 +vn -0.545911 0.790429 -0.277854 +vn -0.545911 0.790429 -0.277854 +vn -0.545911 0.790429 -0.277854 +vn -0.264463 0.503333 -0.822627 +vn -0.264463 0.503333 -0.822627 +vn -0.264463 0.503333 -0.822627 +vn -0.264463 0.503333 -0.822627 +vn 0.071428 0.391121 -0.917563 +vn -0.022348 0.280539 -0.959582 +vn -0.022348 0.280539 -0.959582 +vn -0.022348 0.280539 -0.959582 +vn -0.022348 0.280539 -0.959582 +vn -0.727792 0.633269 -0.263228 +vn -0.727792 0.633269 -0.263228 +vn -0.727792 0.633269 -0.263228 +vn -0.727792 0.633270 -0.263228 +vn 0.024936 0.422951 -0.905809 +vn 0.020457 0.652869 -0.757194 +vn 0.064463 0.552835 -0.830794 +vn -0.064587 0.432978 -0.899088 +vn -0.051952 0.691351 -0.720648 +vn -0.430065 0.475818 -0.767230 +vn -0.430065 0.475818 -0.767230 +vn -0.430065 0.475818 -0.767230 +vn -0.430065 0.475818 -0.767230 +vn 0.015304 0.118231 -0.992868 +vn 0.015304 0.118231 -0.992868 +vn 0.015304 0.118231 -0.992868 +vn 0.015304 0.118231 -0.992868 +vn -0.064587 -0.432978 -0.899088 +vn -0.064587 0.000000 -0.997912 +vn -0.567759 0.818150 -0.090998 +vn -0.567759 0.818150 -0.090998 +vn -0.567759 0.818150 -0.090998 +vn -0.567759 0.818150 -0.090998 +vn -0.755415 0.654260 -0.035929 +vn -0.755415 0.654260 -0.035929 +vn -0.755415 0.654260 -0.035929 +vn -0.755415 0.654260 -0.035929 +vn 0.000000 0.900969 -0.433884 +vn -0.072505 0.898614 -0.432707 +vn -0.082933 0.960669 -0.265023 +vn 0.000000 0.964593 -0.263742 +vn -0.924988 0.164874 0.342364 +vn -0.927519 0.290707 0.234942 +vn -1.000000 -0.000000 0.000000 +vn -0.925651 0.362381 0.108856 +vn -0.924989 -0.000000 -0.379995 +vn -0.924988 -0.164873 -0.342365 +vn -0.924989 -0.297092 -0.236923 +vn -0.924989 -0.370468 -0.084556 +vn -0.924989 -0.370467 0.084557 +vn -0.924989 -0.297092 0.236923 +vn -0.924989 -0.164874 0.342363 +vn -0.924989 0.000000 0.379995 +vn -0.727792 0.633269 0.263229 +vn -0.727792 0.633269 0.263229 +vn -0.727792 0.633269 0.263229 +vn -0.727792 0.633269 0.263229 +vn -0.545911 0.790429 0.277853 +vn -0.545911 0.790429 0.277853 +vn -0.545911 0.790429 0.277853 +vn -0.545911 0.790429 0.277853 +vn -0.755415 0.654260 0.035930 +vn -0.755415 0.654260 0.035930 +vn -0.755415 0.654260 0.035930 +vn -0.755415 0.654260 0.035930 +vn -0.567759 0.818150 0.090998 +vn -0.567759 0.818150 0.090998 +vn -0.567759 0.818150 0.090998 +vn -0.567759 0.818150 0.090998 +vn -0.076076 0.997102 0.000000 +vn -0.082933 0.960669 0.265024 +vn 0.000000 0.964593 0.263742 +vn 0.000000 1.000000 0.000000 +vn -0.942446 0.334359 0.000000 +vn -0.925651 0.362381 -0.108857 +vn -0.927519 0.290707 -0.234942 +vn -0.924989 0.164874 -0.342364 +vn -0.072505 0.898614 0.432707 +vn 0.000000 0.900969 0.433884 +vn 0.070121 -0.000000 0.997539 +vn 0.026848 -0.000000 0.999640 +vn -0.064587 0.000000 0.997912 +vn 0.015304 0.118232 0.992868 +vn 0.015304 0.118232 0.992868 +vn 0.015304 0.118232 0.992868 +vn 0.015304 0.118232 0.992868 +vn -0.022348 0.280539 0.959583 +vn -0.022348 0.280539 0.959583 +vn -0.022348 0.280539 0.959583 +vn -0.022348 0.280539 0.959583 +vn -0.051952 0.691351 0.720648 +vn -0.064587 0.432978 0.899088 +vn 0.024936 0.422951 0.905809 +vn 0.020457 0.652869 0.757194 +vn -0.430065 0.475818 0.767230 +vn -0.430065 0.475818 0.767230 +vn -0.430065 0.475818 0.767230 +vn -0.430065 0.475818 0.767230 +vn -0.264462 0.503332 0.822628 +vn -0.264462 0.503332 0.822628 +vn -0.264462 0.503332 0.822628 +vn -0.264462 0.503332 0.822628 +vn 0.079554 0.432509 0.898113 +vn 0.081210 0.779249 0.621431 +vn 0.083582 0.756709 0.648386 +vn 0.090884 0.864154 0.494952 +vn 0.074379 0.588458 0.805099 +vn 0.299258 0.416890 0.858282 +vn 0.299258 0.416890 0.858282 +vn 0.299258 0.416890 0.858282 +vn 0.299258 0.416890 0.858282 +vn 0.502967 0.815394 0.286628 +vn 0.502967 0.815394 0.286628 +vn 0.502967 0.815394 0.286628 +vn 0.502967 0.815394 0.286628 +vn 0.296862 0.477629 0.826888 +vn 0.296862 0.477629 0.826888 +vn 0.296862 0.477629 0.826888 +vn 0.296862 0.477629 0.826888 +vn 0.071428 0.391121 0.917564 +vn 0.064463 0.552834 0.830794 +vn 0.070946 0.105332 0.991903 +vn 0.070946 0.105332 0.991903 +vn 0.115643 0.269922 0.955913 +vn 0.115643 0.269922 0.955913 +vn 0.115643 0.269922 0.955913 +vn 0.115643 0.269922 0.955913 +vn 0.083582 0.756710 -0.648386 +vn 0.090884 0.864154 -0.494952 +vn 0.074379 0.588458 -0.805099 +vn 0.070946 0.105332 -0.991903 +vn 0.070946 0.105332 -0.991903 +vn 0.299258 0.416891 -0.858281 +vn 0.299258 0.416891 -0.858281 +vn 0.299258 0.416891 -0.858281 +vn 0.299258 0.416891 -0.858281 +vn 0.296862 0.477628 -0.826888 +vn 0.296862 0.477628 -0.826888 +vn 0.296862 0.477628 -0.826888 +vn 0.296862 0.477628 -0.826888 +vn 0.115643 0.269922 -0.955913 +vn 0.115643 0.269922 -0.955913 +vn 0.115643 0.269922 -0.955913 +vn 0.115643 0.269922 -0.955913 +vn 0.102431 0.896230 0.431601 +vn 0.102431 0.896230 0.431601 +vn 0.102431 0.896230 0.431601 +vn 0.102431 0.896230 0.431601 +vn 0.425735 0.904848 0.000000 +vn 0.425735 0.904848 0.000000 +vn 0.425735 0.904848 0.000000 +vn 0.102431 0.896230 -0.431601 +vn 0.102431 0.896230 -0.431601 +vn 0.102431 0.896230 -0.431601 +vn 0.102431 0.896230 -0.431601 +vn 0.968371 0.249514 0.000000 +vn 0.968371 0.249514 0.000000 +vn 0.968371 0.249514 0.000000 +vn 0.004416 0.060621 -0.998151 +vn 0.004416 0.060621 -0.998151 +vn 0.004416 0.060621 -0.998151 +vn 0.004416 0.060621 -0.998151 +vn -0.893202 0.449656 0.000001 +vn -0.893202 0.449656 0.000001 +vn -0.893202 0.449656 0.000001 +vn 0.004416 0.060620 0.998151 +vn 0.004416 0.060620 0.998151 +vn 0.004416 0.060620 0.998151 +vn 0.004416 0.060620 0.998151 +vn 0.043031 -0.112813 0.992684 +vn 0.043031 -0.112813 0.992684 +vn 0.043031 -0.112813 0.992684 +vn 0.043031 -0.112813 0.992684 +vn 0.043030 -0.112812 -0.992684 +vn 0.043030 -0.112812 -0.992684 +vn 0.043030 -0.112812 -0.992684 +vn 0.043030 -0.112812 -0.992684 +vn 0.524265 0.850274 0.046693 +vn 0.524265 0.850274 0.046693 +vn 0.524265 0.850274 0.046693 +vn 0.524265 0.850274 0.046693 +vn 0.521127 0.848248 -0.094347 +vn 0.521127 0.848248 -0.094347 +vn 0.521127 0.848248 -0.094347 +vn 0.521127 0.848248 -0.094347 +vn 0.521127 0.848248 0.094347 +vn 0.521127 0.848248 0.094347 +vn 0.521127 0.848248 0.094346 +vn 0.521127 0.848248 0.094347 +vn 0.090218 0.955033 0.282440 +vn 0.080965 0.951518 0.296745 +vn 0.493255 0.803255 0.333887 +vn 0.493255 0.803255 0.333887 +vn 0.493255 0.803255 0.333887 +vn 0.493255 0.803255 0.333887 +vn 0.102044 0.959117 0.263971 +vn 0.089030 0.996029 0.000000 +vn 0.079187 0.996860 0.000000 +vn 0.080989 0.952672 -0.293015 +vn 0.090239 0.956082 -0.278863 +vn 0.102059 0.960038 -0.260596 +vn 0.099608 0.995027 -0.000000 +vn 0.493255 0.803256 -0.333886 +vn 0.493255 0.803256 -0.333886 +vn 0.493255 0.803256 -0.333886 +vn 0.493255 0.803256 -0.333886 +vn 0.524265 0.850274 -0.046693 +vn 0.524265 0.850274 -0.046693 +vn 0.524265 0.850274 -0.046693 +vn 0.524265 0.850274 -0.046693 +vn 0.502968 0.815395 -0.286627 +vn 0.502968 0.815395 -0.286627 +vn 0.502968 0.815395 -0.286627 +vn 0.502968 0.815395 -0.286627 +vn -0.893202 0.449656 0.000002 +vn -0.893202 0.449656 0.000002 +vn -0.893202 0.449656 0.000002 +s off +f 77/85/1 78/86/2 79/87/3 80/88/4 +f 81/89/5 82/90/6 83/91/7 84/92/8 +s 1 +f 6/20/9 7/21/10 21/36/11 20/35/12 +f 7/21/10 8/22/13 22/37/14 21/36/11 +f 8/22/13 9/23/15 23/38/16 22/37/14 +f 9/23/15 10/24/17 24/39/18 23/38/16 +f 10/24/17 11/25/19 25/40/20 24/39/18 +f 11/25/19 12/26/21 26/41/22 25/40/20 +f 12/26/21 13/27/23 27/42/24 26/41/22 +f 13/27/23 14/28/25 28/43/26 27/42/24 +f 14/28/25 1/29/27 15/44/28 28/43/26 +f 20/35/12 21/36/11 35/51/29 34/50/30 +f 21/36/11 22/37/14 36/52/31 35/51/29 +f 22/37/14 23/38/16 37/53/32 36/52/31 +f 23/38/16 24/39/18 38/54/33 37/53/32 +f 24/39/18 25/40/20 39/55/34 38/54/33 +f 25/40/20 26/41/22 40/56/35 39/55/34 +f 26/41/22 27/42/24 41/57/36 40/56/35 +f 27/42/24 28/43/26 42/58/37 41/57/36 +f 35/51/29 36/52/31 50/67/38 49/66/39 +f 36/52/31 37/53/32 51/68/40 50/67/38 +f 37/53/32 38/54/33 52/69/41 51/68/40 +f 38/54/33 39/55/34 53/70/42 52/69/41 +f 39/55/34 40/56/35 54/71/43 53/70/42 +f 40/56/35 41/57/36 55/72/44 54/71/43 +s off +f 97/105/45 85/93/46 94/102/47 93/101/48 +f 95/103/49 86/94/50 99/107/51 98/106/52 +f 100/108/53 89/97/54 103/111/55 102/110/56 +s 1 +f 49/66/39 50/67/38 64/164/57 63/160/58 +f 50/67/38 51/68/40 65/139/59 64/164/57 +f 51/68/40 52/69/41 66/141/60 65/139/59 +f 52/69/41 53/70/42 67/143/61 66/141/60 +f 53/70/42 54/71/43 68/145/62 67/143/61 +f 54/71/43 55/72/44 69/148/63 68/145/62 +f 65/139/59 66/141/60 135/174/64 133/170/65 +f 66/141/60 67/143/61 137/177/66 135/174/64 +f 67/143/61 68/145/62 139/180/67 137/177/66 +f 68/145/62 69/148/63 141/183/68 139/180/67 +f 2/2/69 1/1/27 71/78/70 +f 3/3/71 2/2/69 71/78/70 +f 5/5/72 4/4/73 71/78/70 +f 6/6/9 5/5/72 71/78/70 +f 7/7/10 6/6/9 71/78/70 +f 8/8/13 7/7/10 71/78/70 +f 9/9/15 8/8/13 71/78/70 +f 10/10/17 9/9/15 71/78/70 +f 11/11/19 10/10/17 71/78/70 +f 12/12/21 11/11/19 71/78/70 +f 13/13/23 12/12/21 71/78/70 +f 14/14/25 13/13/23 71/78/70 +f 1/1/27 14/14/25 71/78/70 +f 1/15/27 2/16/69 78/86/74 77/85/75 +s off +f 2/16/76 16/31/77 79/87/78 78/86/79 +f 16/31/80 15/30/81 80/88/82 79/87/83 +s 1 +f 15/30/28 1/15/27 77/85/84 80/88/85 +f 5/19/72 6/20/9 82/90/86 81/89/87 +f 6/20/9 20/35/12 83/91/88 82/90/89 +f 20/35/12 19/34/90 84/92/91 83/91/92 +s off +f 19/34/93 5/19/94 81/89/95 84/92/96 +f 94/102/97 86/94/98 95/103/99 93/101/100 +f 95/103/101 87/95/102 96/104/103 93/101/104 +f 96/104/105 88/96/106 97/105/107 93/101/108 +f 99/107/109 89/97/110 100/108/111 98/106/112 +f 100/108/113 90/98/114 101/109/115 98/106/116 +f 101/109/117 87/95/118 95/103/119 98/106/120 +f 103/111/121 91/99/122 104/112/123 102/110/124 +f 104/112/125 92/100/126 105/113/127 102/110/128 +f 105/113/129 90/98/130 100/108/131 102/110/132 +f 121/127/133 90/98/134 105/113/135 128/134/136 +f 105/113/137 92/100/138 127/133/139 128/134/140 +s 1 +f 48/65/141 34/50/30 35/51/29 49/66/39 +s off +f 125/131/142 127/133/143 92/100/144 104/112/145 +f 128/134/146 129/150/147 60/167/148 121/127/149 +s 1 +f 62/161/150 61/77/151 126/132/152 48/65/141 +f 63/160/58 62/161/150 48/65/141 49/66/39 +f 148/194/153 146/213/154 61/77/151 62/161/150 +s off +f 128/134/155 127/133/156 61/77/157 129/150/158 +f 126/132/159 61/77/160 127/133/161 125/131/162 +s 1 +f 159/207/163 157/197/164 63/160/58 64/164/57 +f 133/170/65 159/207/163 64/164/57 65/139/59 +s off +f 119/126/165 101/109/166 90/98/167 121/127/168 +f 121/127/169 60/167/170 120/168/171 119/126/172 +s 1 +f 63/160/58 157/197/164 148/194/153 62/161/150 +f 129/150/173 145/190/174 161/209/175 60/167/176 +f 150/214/177 153/200/178 72/166/179 +f 153/200/178 155/186/180 72/166/179 +f 156/162/181 158/163/182 72/166/179 +f 158/163/182 132/140/183 72/166/179 +f 132/140/183 134/142/184 72/166/179 +f 134/142/184 136/144/185 72/166/179 +f 136/144/185 138/146/186 72/166/179 +f 138/146/186 140/185/187 72/166/179 +f 140/185/187 143/203/188 72/166/179 +f 143/203/188 150/214/177 72/166/179 +s off +f 110/118/189 113/120/190 59/159/191 111/137/192 +f 96/104/193 87/95/194 113/120/195 110/118/196 +f 120/168/197 59/159/198 113/120/199 119/126/200 +f 119/126/201 113/120/202 87/95/203 101/109/204 +s 1 +f 163/205/205 154/158/206 59/159/207 120/168/208 +f 155/186/180 162/169/209 72/166/179 +f 160/165/210 144/151/211 72/166/179 +f 144/151/211 147/152/212 72/166/179 +f 147/152/212 156/162/181 72/166/179 +f 154/158/206 152/138/213 111/137/214 59/159/207 +f 161/209/175 163/205/205 120/168/208 60/167/176 +f 72/166/179 162/169/209 160/165/210 +f 55/72/44 41/57/36 42/58/37 56/73/215 +f 69/148/63 55/72/44 56/73/215 70/149/216 +f 142/147/217 141/183/68 69/148/63 70/149/216 +s off +f 112/119/218 58/76/219 115/122/220 114/121/221 +f 114/121/222 97/105/223 88/96/224 112/119/225 +s 1 +f 151/155/226 149/156/227 57/75/228 58/76/229 +f 111/137/214 152/138/213 151/155/226 58/76/229 +f 149/153/227 142/147/217 70/149/216 57/154/228 +s off +f 110/118/230 111/137/231 58/76/232 112/119/233 +f 112/119/234 88/96/235 96/104/236 110/118/237 +s 1 +f 29/45/238 15/30/28 16/31/239 30/46/240 +f 42/58/37 28/43/26 15/44/28 29/59/238 +f 30/46/240 107/115/241 44/61/242 +s off +f 106/114/243 109/117/244 44/61/245 107/115/246 +f 108/116/247 86/94/248 94/102/249 106/114/250 +f 94/102/251 85/93/252 109/117/253 106/114/254 +s 1 +f 44/61/242 43/60/255 29/45/238 30/46/240 +f 43/74/255 56/73/215 42/58/37 29/59/238 +f 115/122/256 58/76/229 57/75/228 43/60/255 +f 57/154/228 70/149/216 56/73/215 43/74/255 +f 115/122/256 44/61/242 109/117/257 114/121/258 +s off +f 114/121/259 109/117/260 85/93/261 97/105/262 +s 1 +f 115/122/256 43/60/255 44/61/242 +f 33/49/263 19/34/90 20/35/12 34/50/30 +f 123/129/264 33/49/263 47/64/265 +f 48/65/141 47/64/265 33/49/263 34/50/30 +f 124/130/266 47/64/265 126/132/152 125/131/267 +s off +f 122/128/268 123/129/269 47/64/270 124/130/271 +f 124/130/272 91/99/273 103/111/274 122/128/275 +f 125/131/276 104/112/277 91/99/278 124/130/279 +s 1 +f 48/65/141 126/132/152 47/64/265 +s off +f 16/31/280 2/16/281 3/17/282 17/32/283 +f 73/81/284 74/79/285 75/80/286 +f 18/33/287 4/18/288 5/19/289 19/34/290 +f 74/84/291 73/82/292 76/83/293 +f 4/18/294 18/33/295 75/80/296 74/79/297 +f 130/135/298 75/80/299 18/33/300 +f 75/80/301 17/32/302 3/17/303 73/81/304 +f 3/3/305 71/78/306 76/83/307 73/82/308 +f 76/83/309 71/78/310 4/4/311 74/84/312 +f 108/116/313 45/62/314 117/124/315 116/123/316 +f 116/123/317 118/125/318 89/97/319 99/107/320 +f 116/123/321 99/107/322 86/94/323 108/116/324 +s 1 +f 31/47/325 30/46/240 16/31/239 17/32/326 +s off +f 106/114/327 107/115/328 45/62/329 108/116/330 +s 1 +f 107/115/241 30/46/240 31/47/325 45/62/331 +f 131/136/332 130/135/333 18/33/334 32/48/335 +f 33/49/263 32/48/335 18/33/334 19/34/90 +f 32/48/335 46/63/336 117/124/337 131/136/332 +s off +f 122/128/338 118/125/339 46/63/340 123/129/341 +f 117/124/342 46/63/343 118/125/344 116/123/345 +f 103/111/346 89/97/347 118/125/348 122/128/349 +s 1 +f 32/48/335 33/49/263 123/129/264 46/63/336 +f 131/136/332 117/124/337 45/62/331 31/47/325 +f 130/135/333 131/136/332 31/47/325 17/32/326 +s off +f 75/80/350 130/135/351 17/32/352 +s 1 +f 146/213/154 145/190/174 129/150/173 61/77/151 +f 133/170/65 132/171/183 158/172/182 159/207/163 +f 132/173/183 133/170/65 135/174/64 134/175/184 +f 134/176/184 135/174/64 137/177/66 136/178/185 +f 136/179/185 137/177/66 139/180/67 138/181/186 +f 138/182/186 139/180/67 141/183/68 140/184/187 +f 140/202/187 141/183/68 142/147/217 143/188/188 +f 143/216/188 142/147/217 149/153/227 150/199/177 +f 145/190/174 144/191/211 160/192/210 161/209/175 +f 144/193/211 146/213/154 148/194/153 147/195/212 +f 147/196/212 148/194/153 157/197/164 156/198/181 +f 150/189/177 149/156/227 151/155/226 153/215/178 +f 153/187/178 152/138/213 154/158/206 155/157/180 +f 155/204/180 154/158/206 163/205/205 162/211/209 +f 156/206/181 157/197/164 159/207/163 158/208/182 +f 161/209/175 160/210/210 162/211/209 163/205/205 +f 144/212/211 145/190/174 146/213/154 +f 151/155/226 152/138/213 153/201/178 +v -4.526886 2.025292 -6.500000 +v -4.526886 2.025292 6.500000 +v -2.453123 1.990687 -6.229743 +v -2.453123 1.990687 6.229743 +v -4.716847 1.835833 -6.500000 +v -4.716847 1.835833 6.500000 +v -2.453123 1.835833 6.229743 +v -2.453123 1.835833 -6.229743 +v -4.526886 2.025292 5.055555 +v -2.453123 1.835833 5.055555 +v -4.853122 1.835833 5.055555 +v -4.526886 2.025292 3.611111 +v -2.453123 1.990687 3.611111 +v -2.453123 1.835833 3.611111 +v -4.853122 1.835833 3.611111 +v -4.526886 2.025292 2.166667 +v -2.453123 1.835833 2.166667 +v -4.853123 1.835833 2.166667 +v -4.526886 2.025292 0.722222 +v -2.453123 1.990687 0.722222 +v -2.453123 1.835833 0.722222 +v -4.853123 1.835833 0.722222 +v -4.526886 2.025292 -0.722222 +v -2.453123 1.990687 -0.722222 +v -2.453123 1.835833 -0.722222 +v -4.853123 1.835833 -0.722222 +v -4.526886 2.025292 -2.166667 +v -2.453123 1.835833 -2.166667 +v -4.853123 1.835833 -2.166667 +v -4.526886 2.025292 -3.611112 +v -2.453123 1.990687 -3.611112 +v -2.453123 1.835833 -3.611112 +v -4.853123 1.835833 -3.611112 +v -4.526886 2.025292 -5.055555 +v -2.453123 1.835833 -5.055556 +v -4.853123 1.835833 -5.055556 +vt 0.616150 0.306151 +vt 0.610251 0.992489 +vt 0.731520 0.325838 +vt 0.725886 0.974777 +vt 0.591047 0.313683 +vt 0.585284 0.984520 +vt 0.476020 0.976054 +vt 0.481594 0.320254 +vt 0.605471 0.315123 +vt 0.599736 0.983336 +vt 0.717845 0.974434 +vt 0.723467 0.326044 +vt 0.579649 0.910291 +vt 0.724217 0.318026 +vt 0.720447 0.913504 +vt 0.472481 0.914051 +vt 0.598060 0.909419 +vt 0.578999 0.836511 +vt 0.718456 0.982458 +vt 0.730267 0.838710 +vt 0.471391 0.837979 +vt 0.722220 0.838546 +vt 0.598433 0.836444 +vt 0.579018 0.761838 +vt 0.602520 0.389018 +vt 0.723365 0.763360 +vt 0.470415 0.761959 +vt 0.598696 0.761928 +vt 0.579544 0.686680 +vt 0.476998 0.382224 +vt 0.732302 0.688095 +vt 0.471265 0.685937 +vt 0.724215 0.688011 +vt 0.599234 0.686836 +vt 0.580196 0.611444 +vt 0.725022 0.387043 +vt 0.732967 0.612676 +vt 0.471912 0.610366 +vt 0.724881 0.612612 +vt 0.599876 0.611634 +vt 0.580951 0.536288 +vt 0.584126 0.387830 +vt 0.725344 0.537251 +vt 0.472346 0.534318 +vt 0.600626 0.536533 +vt 0.582223 0.461596 +vt 0.601648 0.461993 +vt 0.733547 0.462028 +vt 0.474637 0.458282 +vt 0.725502 0.462044 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.050020 0.997007 0.058958 +vn 0.050020 0.997007 0.058958 +vn 0.050020 0.997007 0.058958 +vn 0.050020 0.997007 0.058958 +vn -0.591510 0.805815 -0.027903 +vn -0.591510 0.805815 -0.027903 +vn -0.591510 0.805815 -0.027903 +vn -0.591510 0.805815 -0.027903 +vn 0.122841 -0.067773 0.990110 +vn 0.122841 -0.067773 0.990110 +vn 0.122841 -0.067773 0.990110 +vn 0.122841 -0.067773 0.990110 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.122841 -0.067773 -0.990110 +vn 0.122841 -0.067773 -0.990110 +vn 0.122841 -0.067773 -0.990110 +vn 0.122841 -0.067773 -0.990110 +vn 0.050020 0.997006 -0.058958 +vn 0.050020 0.997007 -0.058958 +vn 0.050020 0.997007 -0.058958 +vn 0.050020 0.997007 -0.058958 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn -0.591512 0.805814 0.027903 +vn -0.591512 0.805814 0.027903 +vn -0.591511 0.805814 0.027903 +vn -0.591512 0.805814 0.027903 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502197 0.864753 0.000000 +vn -0.502197 0.864753 0.000000 +vn -0.502197 0.864753 0.000000 +vn -0.502197 0.864753 0.000000 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn 0.016684 0.999861 0.000000 +vn 0.016684 0.999861 0.000000 +vn 0.016684 0.999861 0.000000 +vn 0.016684 0.999861 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +s off +f 168/225/353 171/228/354 198/252/355 199/241/356 +f 164/221/357 197/258/358 198/246/359 166/224/360 +f 164/221/361 168/225/362 199/241/363 197/258/364 +f 165/218/365 169/226/366 170/227/367 167/235/368 +f 198/252/369 171/228/370 166/219/371 +f 166/230/372 171/228/373 168/225/374 164/217/375 +f 172/229/376 165/222/377 167/223/378 173/232/379 +f 167/220/380 170/227/381 173/231/382 +f 174/233/383 173/231/384 170/227/385 169/226/386 +f 172/229/387 174/233/388 169/226/389 165/222/390 +f 175/234/391 172/229/392 173/232/393 176/237/394 +f 173/231/395 177/238/396 176/236/397 +f 178/239/398 177/238/399 173/231/400 174/233/401 +f 175/234/402 178/239/403 174/233/404 172/229/405 +f 179/240/406 175/234/407 176/237/408 180/243/409 +f 176/236/410 177/238/411 180/242/412 +f 181/244/413 180/242/414 177/238/415 178/239/416 +f 179/240/417 181/244/418 178/239/419 175/234/420 +f 182/245/421 179/240/422 180/243/423 183/248/424 +f 180/242/425 184/249/426 183/247/427 +f 185/250/428 184/249/429 180/242/430 181/244/431 +f 182/245/432 185/250/433 181/244/434 179/240/435 +f 186/251/436 182/245/437 183/248/438 187/254/439 +f 183/247/440 184/249/441 188/255/442 187/253/443 +f 189/256/444 188/255/445 184/249/446 185/250/447 +f 186/251/448 189/256/449 185/250/450 182/245/451 +f 190/257/452 186/251/453 187/254/454 191/260/455 +f 187/253/456 188/255/457 191/259/458 +f 192/261/459 191/259/460 188/255/461 189/256/462 +f 190/257/463 192/261/464 189/256/465 186/251/466 +f 193/262/467 190/257/468 191/260/469 194/265/470 +f 191/259/471 195/266/472 194/264/473 +f 196/263/474 195/266/475 191/259/476 192/261/477 +f 193/262/478 196/263/479 192/261/480 190/257/481 +f 197/258/482 193/262/483 194/265/484 198/246/485 +f 194/264/486 195/266/487 198/252/488 +f 199/241/489 198/252/490 195/266/491 196/263/492 +f 197/258/493 199/241/494 196/263/495 193/262/496 +v -3.559415 -0.491252 -6.500000 +v -3.559415 -0.491252 6.500000 +v -1.485652 -0.525856 -6.229743 +v -1.485653 -0.525856 6.229743 +v -3.749376 -0.680711 -6.500000 +v -3.749376 -0.680711 6.500000 +v -1.485653 -0.680711 6.229743 +v -1.485652 -0.680711 -6.229743 +v -3.559415 -0.491252 5.055555 +v -1.485652 -0.680711 5.055555 +v -3.885653 -0.680711 5.055555 +v -3.559415 -0.491252 3.611110 +v -1.485652 -0.525856 3.611110 +v -1.485652 -0.680711 3.611110 +v -3.885652 -0.680711 3.611110 +v -3.559415 -0.491252 2.166667 +v -1.485652 -0.680711 2.166667 +v -3.885653 -0.680711 2.166667 +v -3.559415 -0.491252 0.722222 +v -1.485652 -0.525856 0.722222 +v -1.485652 -0.680711 0.722222 +v -3.885652 -0.680711 0.722222 +v -3.559416 -0.491252 -0.722223 +v -1.485652 -0.525856 -0.722222 +v -1.485652 -0.680711 -0.722222 +v -3.885653 -0.680711 -0.722223 +v -3.559416 -0.491252 -2.166667 +v -1.485652 -0.680711 -2.166667 +v -3.885653 -0.680711 -2.166667 +v -3.559415 -0.491252 -3.611112 +v -1.485652 -0.525856 -3.611111 +v -1.485652 -0.680711 -3.611111 +v -3.885653 -0.680711 -3.611111 +v -3.559415 -0.491252 -5.055556 +v -1.485652 -0.680711 -5.055556 +v -3.885652 -0.680711 -5.055556 +vt 0.878803 0.306151 +vt 0.872904 0.992489 +vt 0.994173 0.325838 +vt 0.988539 0.974777 +vt 0.853700 0.313683 +vt 0.847937 0.984520 +vt 0.738673 0.976054 +vt 0.744247 0.320254 +vt 0.868124 0.315123 +vt 0.862389 0.983336 +vt 0.980498 0.974434 +vt 0.986120 0.326044 +vt 0.842302 0.910291 +vt 0.986870 0.318026 +vt 0.983099 0.913504 +vt 0.735134 0.914051 +vt 0.860713 0.909419 +vt 0.841652 0.836511 +vt 0.981109 0.982458 +vt 0.992920 0.838710 +vt 0.734044 0.837979 +vt 0.984873 0.838546 +vt 0.861086 0.836444 +vt 0.841671 0.761838 +vt 0.865172 0.389018 +vt 0.986018 0.763360 +vt 0.733068 0.761959 +vt 0.861349 0.761928 +vt 0.842197 0.686680 +vt 0.739650 0.382224 +vt 0.994955 0.688095 +vt 0.733917 0.685937 +vt 0.986868 0.688011 +vt 0.861887 0.686836 +vt 0.842849 0.611444 +vt 0.987675 0.387043 +vt 0.995620 0.612676 +vt 0.734565 0.610366 +vt 0.987534 0.612612 +vt 0.862529 0.611634 +vt 0.843604 0.536288 +vt 0.846779 0.387830 +vt 0.987996 0.537251 +vt 0.734999 0.534318 +vt 0.863279 0.536533 +vt 0.844876 0.461596 +vt 0.864301 0.461993 +vt 0.996200 0.462028 +vt 0.737290 0.458282 +vt 0.988155 0.462044 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.050020 0.997007 0.058959 +vn 0.050020 0.997007 0.058959 +vn 0.050020 0.997006 0.058959 +vn 0.050020 0.997007 0.058959 +vn -0.591510 0.805815 -0.027903 +vn -0.591510 0.805815 -0.027903 +vn -0.591510 0.805815 -0.027903 +vn -0.591510 0.805815 -0.027903 +vn 0.122841 -0.067773 0.990110 +vn 0.122841 -0.067773 0.990110 +vn 0.122841 -0.067773 0.990110 +vn 0.122841 -0.067773 0.990110 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 0.122841 -0.067772 -0.990110 +vn 0.122841 -0.067772 -0.990110 +vn 0.122841 -0.067772 -0.990110 +vn 0.122841 -0.067772 -0.990110 +vn 0.050020 0.997007 -0.058959 +vn 0.050020 0.997007 -0.058959 +vn 0.050020 0.997007 -0.058959 +vn 0.050020 0.997007 -0.058959 +vn 1.000000 0.000000 0.000001 +vn 1.000000 0.000000 0.000001 +vn 1.000000 0.000000 0.000001 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.591510 0.805815 0.027903 +vn -0.591510 0.805815 0.027903 +vn -0.591510 0.805815 0.027903 +vn -0.591510 0.805815 0.027903 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn -0.502195 0.864754 -0.000000 +vn -0.502195 0.864754 -0.000000 +vn -0.502195 0.864754 -0.000000 +vn -0.502195 0.864754 -0.000000 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn 0.016684 0.999861 0.000000 +vn 0.016684 0.999861 0.000000 +vn 0.016684 0.999861 0.000000 +vn 0.016684 0.999861 0.000000 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn -0.502196 0.864754 0.000000 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 0.053867 0.997117 0.053449 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 1.000000 0.000000 -0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 0.053867 0.997117 -0.053449 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn 0.000000 -1.000000 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +vn -0.502196 0.864754 -0.000000 +s off +f 204/275/497 207/278/498 234/302/499 235/291/500 +f 200/271/501 233/308/502 234/296/503 202/274/504 +f 200/271/505 204/275/506 235/291/507 233/308/508 +f 201/268/509 205/276/510 206/277/511 203/285/512 +f 234/302/513 207/278/514 202/269/515 +f 202/280/516 207/278/517 204/275/518 200/267/519 +f 208/279/520 201/272/521 203/273/522 209/282/523 +f 203/270/524 206/277/525 209/281/526 +f 210/283/527 209/281/528 206/277/529 205/276/530 +f 208/279/531 210/283/532 205/276/533 201/272/534 +f 211/284/535 208/279/536 209/282/537 212/287/538 +f 209/281/539 213/288/540 212/286/541 +f 214/289/542 213/288/543 209/281/544 210/283/545 +f 211/284/546 214/289/547 210/283/548 208/279/549 +f 215/290/550 211/284/551 212/287/552 216/293/553 +f 212/286/554 213/288/555 216/292/556 +f 217/294/557 216/292/558 213/288/559 214/289/560 +f 215/290/561 217/294/562 214/289/563 211/284/564 +f 218/295/565 215/290/566 216/293/567 219/298/568 +f 216/292/569 220/299/570 219/297/571 +f 221/300/572 220/299/573 216/292/574 217/294/575 +f 218/295/576 221/300/577 217/294/578 215/290/579 +f 222/301/580 218/295/581 219/298/582 223/304/583 +f 219/297/584 220/299/585 224/305/586 223/303/587 +f 225/306/588 224/305/589 220/299/590 221/300/591 +f 222/301/592 225/306/593 221/300/594 218/295/595 +f 226/307/596 222/301/597 223/304/598 227/310/599 +f 223/303/600 224/305/601 227/309/602 +f 228/311/603 227/309/604 224/305/605 225/306/606 +f 226/307/607 228/311/608 225/306/609 222/301/610 +f 229/312/611 226/307/612 227/310/613 230/315/614 +f 227/309/615 231/316/616 230/314/617 +f 232/313/618 231/316/619 227/309/620 228/311/621 +f 229/312/622 232/313/623 228/311/624 226/307/625 +f 233/308/626 229/312/627 230/315/628 234/296/629 +f 230/314/630 231/316/631 234/302/632 +f 235/291/633 234/302/634 231/316/635 232/313/636 +f 233/308/637 235/291/638 232/313/639 229/312/640 +v -4.142218 0.492005 -0.689421 +v -4.278798 0.483220 -0.711072 +v -4.229631 0.565988 -0.611412 +v -4.039907 1.892682 -1.903144 +v -4.176487 1.883898 -1.924795 +v -4.127320 1.966665 -1.825134 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn 0.159359 -0.653124 -0.740293 +vn 0.159359 -0.653124 -0.740293 +vn 0.159359 -0.653124 -0.740293 +vn 0.159359 -0.653124 -0.740293 +vn -0.933302 0.271664 0.234831 +vn -0.933302 0.271664 0.234831 +vn -0.933302 0.271664 0.234831 +vn -0.933302 0.271664 0.234831 +vn 0.773945 0.381464 0.505464 +vn 0.773945 0.381464 0.505464 +vn 0.773945 0.381464 0.505464 +vn 0.773945 0.381464 0.505464 +s off +f 236/317/641 237/318/642 240/322/643 239/321/644 +f 237/318/645 238/319/646 241/323/647 240/322/648 +f 238/319/649 236/320/650 239/324/651 241/323/652 +v -3.239272 0.526544 -0.759048 +v -3.375609 0.514178 -0.780479 +v -3.327950 0.606955 -0.689259 +v -3.143238 1.854496 -2.131890 +v -3.279576 1.842130 -2.153321 +v -3.231915 1.934906 -2.062101 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn 0.171460 -0.714084 -0.678738 +vn 0.171460 -0.714084 -0.678738 +vn 0.171460 -0.714084 -0.678738 +vn 0.171460 -0.714084 -0.678738 +vn -0.937813 0.279982 0.205223 +vn -0.937813 0.279982 0.205223 +vn -0.937813 0.279982 0.205223 +vn -0.937813 0.279982 0.205223 +vn 0.766343 0.434134 0.473546 +vn 0.766343 0.434134 0.473546 +vn 0.766343 0.434134 0.473546 +vn 0.766343 0.434134 0.473546 +s off +f 242/325/653 243/326/654 246/330/655 245/329/656 +f 243/326/657 244/327/658 247/331/659 246/330/660 +f 244/327/661 242/328/662 245/332/663 247/331/664 +v -3.163166 -0.512404 -4.366673 +v -3.288760 -0.570938 -4.366673 +v -3.225963 -0.541671 -4.246673 +v -4.291037 1.907626 -4.366673 +v -4.416630 1.849092 -4.366673 +v -4.353833 1.878359 -4.246673 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn 0.000000 -0.000001 -1.000000 +vn 0.000000 -0.000001 -1.000000 +vn 0.000000 -0.000001 -1.000000 +vn 0.000000 -0.000001 -1.000000 +vn -0.784961 -0.365836 0.500000 +vn -0.784961 -0.365836 0.500000 +vn -0.784961 -0.365836 0.500000 +vn -0.784961 -0.365836 0.500000 +vn 0.784961 0.365837 0.500000 +vn 0.784961 0.365837 0.500000 +vn 0.784961 0.365837 0.500000 +vn 0.784961 0.365837 0.500000 +s off +f 248/333/665 249/334/666 252/338/667 251/337/668 +f 249/334/669 250/335/670 253/339/671 252/338/672 +f 250/335/673 248/336/674 251/340/675 253/339/676 +v -2.090913 -0.552991 -4.366673 +v -2.216506 -0.611525 -4.366673 +v -2.153710 -0.582258 -4.246673 +v -3.269892 1.976702 -4.366673 +v -3.395485 1.918168 -4.366673 +v -3.332688 1.947435 -4.246673 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn -0.000005 0.000000 -1.000000 +vn -0.000005 0.000000 -1.000000 +vn -0.000005 0.000000 -1.000000 +vn -0.000005 0.000000 -1.000000 +vn -0.784960 -0.365836 0.500001 +vn -0.784960 -0.365836 0.500001 +vn -0.784960 -0.365836 0.500001 +vn -0.784960 -0.365836 0.500001 +vn 0.784962 0.365835 0.499999 +vn 0.784962 0.365835 0.499999 +vn 0.784962 0.365835 0.499999 +vn 0.784962 0.365835 0.499999 +s off +f 254/341/677 255/342/678 258/346/679 257/345/680 +f 255/342/681 256/343/682 259/347/683 258/346/684 +f 256/343/685 254/344/686 257/348/687 259/347/688 +v -4.142218 0.492005 0.689421 +v -4.278798 0.483220 0.711072 +v -4.229631 0.565988 0.611412 +v -4.039907 1.892682 1.903144 +v -4.176487 1.883898 1.924795 +v -4.127320 1.966665 1.825134 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn -0.159359 0.653124 -0.740293 +vn -0.159359 0.653124 -0.740293 +vn -0.159359 0.653124 -0.740293 +vn -0.159359 0.653124 -0.740293 +vn 0.933302 -0.271664 0.234831 +vn 0.933302 -0.271664 0.234831 +vn 0.933302 -0.271664 0.234831 +vn 0.933302 -0.271664 0.234831 +vn -0.773945 -0.381464 0.505464 +vn -0.773945 -0.381464 0.505464 +vn -0.773945 -0.381464 0.505464 +vn -0.773945 -0.381464 0.505464 +s off +f 260/349/689 261/350/690 264/354/691 263/353/692 +f 261/350/693 262/351/694 265/355/695 264/354/696 +f 262/351/697 260/352/698 263/356/699 265/355/700 +v -3.239272 0.526544 0.759048 +v -3.375609 0.514178 0.780479 +v -3.327950 0.606955 0.689259 +v -3.143239 1.854496 2.131890 +v -3.279576 1.842130 2.153321 +v -3.231915 1.934906 2.062101 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn -0.171462 0.714082 -0.678739 +vn -0.171462 0.714082 -0.678739 +vn -0.171462 0.714082 -0.678739 +vn -0.171462 0.714082 -0.678739 +vn 0.937813 -0.279982 0.205223 +vn 0.937813 -0.279982 0.205223 +vn 0.937813 -0.279982 0.205223 +vn 0.937813 -0.279982 0.205223 +vn -0.766344 -0.434134 0.473546 +vn -0.766344 -0.434133 0.473546 +vn -0.766344 -0.434134 0.473546 +vn -0.766344 -0.434133 0.473546 +s off +f 266/357/701 267/358/702 270/362/703 269/361/704 +f 267/358/705 268/359/706 271/363/707 270/362/708 +f 268/359/709 266/360/710 269/364/711 271/363/712 +v -3.163167 -0.512404 4.366673 +v -3.288760 -0.570938 4.366673 +v -3.225963 -0.541671 4.246673 +v -4.291037 1.907626 4.366672 +v -4.416630 1.849092 4.366673 +v -4.353833 1.878359 4.246673 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn 0.000000 -0.000001 -1.000000 +vn 0.000000 -0.000001 -1.000000 +vn 0.000000 -0.000001 -1.000000 +vn 0.000000 -0.000001 -1.000000 +vn 0.784961 0.365836 0.500000 +vn 0.784961 0.365836 0.500000 +vn 0.784961 0.365836 0.500000 +vn 0.784961 0.365836 0.500000 +vn -0.784963 -0.365836 0.499998 +vn -0.784963 -0.365836 0.499998 +vn -0.784963 -0.365836 0.499998 +vn -0.784963 -0.365836 0.499998 +s off +f 272/365/713 273/366/714 276/370/715 275/369/716 +f 273/366/717 274/367/718 277/371/719 276/370/720 +f 274/367/721 272/368/722 275/372/723 277/371/724 +v -2.090913 -0.552991 4.366673 +v -2.216506 -0.611525 4.366673 +v -2.153710 -0.582258 4.246673 +v -3.269892 1.976702 4.366673 +v -3.395485 1.918168 4.366673 +v -3.332688 1.947435 4.246673 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn 0.000005 0.000000 -1.000000 +vn 0.000005 0.000000 -1.000000 +vn 0.000005 0.000000 -1.000000 +vn 0.000005 0.000000 -1.000000 +vn 0.784961 0.365837 0.499999 +vn 0.784961 0.365837 0.499999 +vn 0.784961 0.365837 0.499999 +vn 0.784961 0.365837 0.499999 +vn -0.784962 -0.365835 0.500000 +vn -0.784962 -0.365835 0.500000 +vn -0.784962 -0.365835 0.500000 +vn -0.784962 -0.365835 0.500000 +s off +f 278/373/725 279/374/726 282/378/727 281/377/728 +f 279/374/729 280/375/730 283/379/731 282/378/732 +f 280/375/733 278/376/734 281/380/735 283/379/736 +v -5.517155 0.803553 -1.563271 +v -5.487778 -0.252819 -0.145963 +v -5.274500 -0.974333 1.464671 +v -5.274499 0.952056 -1.477533 +v -5.487779 0.252819 0.145963 +v -5.517155 -0.781276 1.576133 +v -5.303876 0.252819 0.145963 +v -5.303876 -0.252819 -0.145963 +v -5.502548 0.333527 -0.821039 +v -5.374407 0.570041 -0.684488 +v -5.289107 0.544277 -0.699363 +v -5.417247 0.307763 -0.835914 +v -5.374407 -0.585636 0.675485 +v -5.502548 -0.320803 0.828386 +v -5.417247 -0.292169 0.844918 +v -5.289107 -0.557001 0.692017 +vt 0.751894 0.271249 +vt 0.688242 0.270269 +vt 0.624590 0.269288 +vt 0.742593 0.186217 +vt 0.689223 0.206617 +vt 0.636506 0.184582 +vt 0.690203 0.142965 +vt 0.723500 0.047977 +vt 0.691184 0.079313 +vt 0.659848 0.046996 +vt 0.692165 0.015661 +vt 0.715908 0.196417 +vt 0.720068 0.270759 +vt 0.716398 0.164591 +vt 0.707342 0.063645 +vt 0.707832 0.031819 +vt 0.662864 0.195599 +vt 0.656416 0.269778 +vt 0.663355 0.163773 +vt 0.675516 0.063154 +vt 0.676006 0.031328 +vn -0.990061 0.139469 -0.018114 +vn -0.822500 0.528726 0.209628 +vn 0.317694 0.344286 -0.883481 +vn -0.317702 0.592979 -0.739893 +vn 0.822500 -0.445906 -0.353075 +vn 0.990061 -0.054049 -0.129840 +vn -0.948960 0.273223 0.157558 +vn -0.948215 -0.273253 -0.161933 +vn -0.991413 -0.129753 0.016270 +vn -0.842918 -0.500011 -0.198694 +vn 0.260994 -0.414547 0.871799 +vn -0.260998 -0.547769 0.794877 +vn 0.948215 0.276863 0.155677 +vn 0.842917 0.422079 0.333676 +vn 0.948960 -0.273060 -0.157838 +vn 0.991413 0.050787 0.120506 +s 1 +f 292/393/737 293/392/738 287/384/739 284/381/740 +f 295/395/741 292/396/737 284/388/740 +f 294/394/742 295/395/741 284/388/740 287/384/739 +f 293/392/738 294/394/742 287/384/739 +f 288/385/743 293/392/738 292/393/737 285/382/744 +f 297/397/745 296/398/746 286/383/747 289/386/748 +f 290/387/749 294/394/742 293/392/738 288/385/743 +f 298/399/750 297/397/745 289/386/748 +f 291/389/751 295/395/741 294/394/742 290/387/749 +f 299/400/752 298/399/750 289/386/748 286/390/747 +f 285/391/744 292/396/737 295/395/741 291/389/751 +f 296/401/746 299/400/752 286/390/747 +f 296/398/746 297/397/745 288/385/743 285/382/744 +f 299/400/752 296/401/746 285/391/744 291/389/751 +f 298/399/750 299/400/752 291/389/751 290/387/749 +f 297/397/745 298/399/750 290/387/749 288/385/743 +v -4.728155 0.086603 0.050000 +v -4.728154 0.086603 -0.050000 +v -4.728155 0.000000 -0.100000 +v -4.728154 -0.086603 -0.050000 +v -4.728155 -0.086603 0.050000 +v -4.728154 0.000000 0.100000 +v -5.628155 0.086603 0.050000 +v -5.628154 0.086603 -0.050000 +v -5.628155 0.000000 -0.100000 +v -5.628154 -0.086603 -0.050000 +v -5.628155 -0.086603 0.050000 +v -5.628154 0.000000 0.100000 +v -5.628154 0.000000 -0.000000 +vt 0.988488 0.024988 +vt 0.988520 0.041334 +vt 0.988552 0.057679 +vt 0.988584 0.074025 +vt 0.988616 0.090371 +vt 0.988648 0.106717 +vt 0.988680 0.123062 +vt 0.841008 0.025277 +vt 0.841040 0.041623 +vt 0.841072 0.057968 +vt 0.841104 0.074314 +vt 0.841136 0.090659 +vt 0.841168 0.107005 +vt 0.841200 0.123351 +vt 0.830926 0.091661 +vt 0.830824 0.039274 +vt 0.785404 0.013169 +vt 0.740087 0.039452 +vt 0.740190 0.091839 +vt 0.785609 0.117943 +vt 0.787602 0.065552 +vn 0.000000 1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +vn -0.000000 0.499997 -0.866027 +vn -0.000000 0.499997 -0.866027 +vn -0.000000 0.499997 -0.866027 +vn -0.000000 0.499997 -0.866027 +vn 0.000000 -0.499999 -0.866026 +vn 0.000000 -0.499999 -0.866026 +vn 0.000000 -0.499999 -0.866026 +vn 0.000000 -0.499999 -0.866026 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.499997 0.866027 +vn 0.000000 -0.499997 0.866027 +vn 0.000000 -0.499997 0.866027 +vn 0.000000 -0.499997 0.866027 +vn 0.000000 0.499999 0.866026 +vn 0.000000 0.499999 0.866026 +vn 0.000000 0.499999 0.866026 +vn 0.000000 0.499999 0.866026 +vn -1.000000 -0.000007 -0.000005 +vn -1.000000 0.000003 -0.000002 +vn -1.000000 0.000001 -0.000002 +vn -1.000000 0.000003 0.000005 +vn -1.000000 0.000000 -0.000002 +vn -1.000000 0.000007 -0.000005 +vn -1.000000 0.000000 0.000000 +s off +f 300/402/753 301/403/754 307/410/755 306/409/756 +f 301/403/757 302/404/758 308/411/759 307/410/760 +f 302/404/761 303/405/762 309/412/763 308/411/764 +f 303/405/765 304/406/766 310/413/767 309/412/768 +f 304/406/769 305/407/770 311/414/771 310/413/772 +f 305/407/773 300/408/774 306/415/775 311/414/776 +s 1 +f 306/420/777 307/419/778 312/422/779 +f 307/419/778 308/418/780 312/422/779 +f 308/418/780 309/417/781 312/422/779 +f 309/417/781 310/416/782 312/422/779 +f 310/416/782 311/421/783 312/422/779 +f 311/421/783 306/420/777 312/422/779 +v -3.373292 -2.034549 -1.257066 +v -3.511857 -2.034549 -1.257066 +v -3.442574 -2.094549 -1.153143 +v -3.373292 -0.302498 -0.257066 +v -3.511857 -0.302498 -0.257066 +v -3.442574 -0.362498 -0.153143 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn 0.866025 0.250000 -0.433013 +vn -0.866027 0.250000 -0.433011 +vn -0.866026 0.250000 -0.433011 +vn 0.866025 0.250001 -0.433013 +vn -0.000000 -0.499999 0.866026 +vn -0.000000 -0.499999 0.866026 +s 1 +f 313/423/784 314/424/785 317/428/786 316/427/787 +f 314/424/785 315/425/788 318/429/789 317/428/786 +f 315/425/788 313/426/784 316/430/787 318/429/789 +v -3.250543 -1.665037 -1.076869 +v -3.141562 -1.815037 -1.076869 +v -3.141562 -2.000447 -1.076869 +v -3.250544 -2.150447 -1.076869 +v -3.426880 -2.207742 -1.076869 +v -3.603215 -2.150447 -1.076869 +v -3.712196 -2.000447 -1.076869 +v -3.712196 -1.815037 -1.076869 +v -3.603215 -1.665037 -1.076869 +v -3.426879 -1.607742 -1.076869 +v -3.250543 -1.665037 -1.376869 +v -3.141562 -1.815037 -1.376869 +v -3.141562 -2.000447 -1.376869 +v -3.250544 -2.150447 -1.376869 +v -3.426880 -2.207742 -1.376870 +v -3.603215 -2.150447 -1.376869 +v -3.712196 -2.000447 -1.376870 +v -3.712196 -1.815037 -1.376869 +v -3.603215 -1.665037 -1.376869 +v -3.426879 -1.607742 -1.376869 +v -3.426879 -1.907742 -1.076869 +v -3.426879 -1.907742 -1.271261 +vt 0.252891 0.122706 +vt 0.283344 0.107201 +vt 0.298868 0.076756 +vt 0.293532 0.043002 +vt 0.269375 0.018830 +vt 0.235623 0.013474 +vt 0.205170 0.028980 +vt 0.189646 0.059424 +vt 0.194982 0.093179 +vt 0.219140 0.117351 +vt 0.987153 0.141536 +vt 0.986865 0.156219 +vt 0.986578 0.170901 +vt 0.986290 0.185584 +vt 0.986003 0.200266 +vt 0.985715 0.214949 +vt 0.985428 0.229631 +vt 0.985140 0.244313 +vt 0.984853 0.258995 +vt 0.984565 0.273678 +vt 0.984278 0.288360 +vt 0.766365 0.137213 +vt 0.766077 0.151896 +vt 0.765790 0.166578 +vt 0.765503 0.181260 +vt 0.765215 0.195943 +vt 0.764927 0.210625 +vt 0.764640 0.225307 +vt 0.764352 0.239990 +vt 0.764065 0.254672 +vt 0.763777 0.269355 +vt 0.763490 0.284037 +vt 0.498215 0.317674 +vt 0.521155 0.305994 +vt 0.532848 0.283062 +vt 0.528828 0.257636 +vt 0.510632 0.239429 +vt 0.485208 0.235394 +vt 0.462269 0.247074 +vt 0.450576 0.270006 +vt 0.454595 0.295432 +vt 0.472792 0.313640 +vt 0.246228 0.069095 +vt 0.493196 0.277291 +vn 0.545745 0.751153 0.371391 +vn 0.883034 0.286915 0.371391 +vn 0.859567 0.279290 -0.427950 +vn 0.531242 0.731191 -0.427951 +vn 0.883033 -0.286916 0.371391 +vn 0.859567 -0.279291 -0.427951 +vn 0.545744 -0.751154 0.371391 +vn 0.531242 -0.731191 -0.427951 +vn -0.000000 -0.928477 0.371390 +vn -0.000000 -0.903802 -0.427952 +vn -0.545746 -0.751153 0.371391 +vn -0.531243 -0.731191 -0.427950 +vn -0.883034 -0.286914 0.371391 +vn -0.859567 -0.279289 -0.427952 +vn -0.883034 0.286915 0.371390 +vn -0.859567 0.279290 -0.427951 +vn -0.545745 0.751153 0.371391 +vn -0.531242 0.731191 -0.427951 +vn 0.000000 0.928477 0.371390 +vn 0.000000 0.903802 -0.427951 +vn -0.000000 -0.000000 1.000000 +vn 0.000000 0.000000 -1.000000 +s 1 +f 319/441/790 320/442/791 330/453/792 329/452/793 +f 320/442/791 321/443/794 331/454/795 330/453/792 +f 321/443/794 322/444/796 332/455/797 331/454/795 +f 322/444/796 323/445/798 333/456/799 332/455/797 +f 323/445/798 324/446/800 334/457/801 333/456/799 +f 324/446/800 325/447/802 335/458/803 334/457/801 +f 325/447/802 326/448/804 336/459/805 335/458/803 +f 326/448/804 327/449/806 337/460/807 336/459/805 +f 327/449/806 328/450/808 338/461/809 337/460/807 +f 328/450/808 319/451/790 329/462/793 338/461/809 +f 320/432/791 319/431/790 339/473/810 +f 321/433/794 320/432/791 339/473/810 +f 322/434/796 321/433/794 339/473/810 +f 323/435/798 322/434/796 339/473/810 +f 324/436/800 323/435/798 339/473/810 +f 325/437/802 324/436/800 339/473/810 +f 326/438/804 325/437/802 339/473/810 +f 327/439/806 326/438/804 339/473/810 +f 328/440/808 327/439/806 339/473/810 +f 319/431/790 328/440/808 339/473/810 +f 329/471/793 330/470/792 340/474/811 +f 330/470/792 331/469/795 340/474/811 +f 331/469/795 332/468/797 340/474/811 +f 332/468/797 333/467/799 340/474/811 +f 333/467/799 334/466/801 340/474/811 +f 334/466/801 335/465/803 340/474/811 +f 335/465/803 336/464/805 340/474/811 +f 336/464/805 337/463/807 340/474/811 +f 337/463/807 338/472/809 340/474/811 +f 338/472/809 329/471/793 340/474/811 +v -3.373292 -2.034549 1.257066 +v -3.511857 -2.034549 1.257066 +v -3.442574 -2.094549 1.153143 +v -3.373292 -0.302498 0.257066 +v -3.511857 -0.302498 0.257066 +v -3.442574 -0.362498 0.153143 +vt 0.475306 0.012993 +vt 0.524059 0.012993 +vt 0.572811 0.012993 +vt 0.621564 0.012993 +vt 0.475306 0.232930 +vt 0.524059 0.232930 +vt 0.572811 0.232930 +vt 0.621564 0.232930 +vn -0.866025 -0.250000 -0.433013 +vn 0.866027 -0.249999 -0.433011 +vn 0.866026 -0.249999 -0.433011 +vn -0.866025 -0.250001 -0.433014 +vn 0.000000 0.500000 0.866025 +vn -0.000000 0.500000 0.866026 +s 1 +f 341/475/812 342/476/813 345/480/814 344/479/815 +f 342/476/813 343/477/816 346/481/817 345/480/814 +f 343/477/816 341/478/812 344/482/815 346/481/817 +v -3.250543 -1.665037 1.076869 +v -3.141562 -1.815037 1.076869 +v -3.141562 -2.000447 1.076869 +v -3.250543 -2.150447 1.076869 +v -3.426879 -2.207742 1.076869 +v -3.603214 -2.150447 1.076869 +v -3.712196 -2.000447 1.076869 +v -3.712196 -1.815037 1.076869 +v -3.603214 -1.665037 1.076869 +v -3.426879 -1.607742 1.076869 +v -3.250544 -1.665037 1.376869 +v -3.141562 -1.815037 1.376869 +v -3.141562 -2.000447 1.376869 +v -3.250544 -2.150447 1.376869 +v -3.426879 -2.207742 1.376869 +v -3.603215 -2.150447 1.376869 +v -3.712196 -2.000447 1.376869 +v -3.712196 -1.815037 1.376869 +v -3.603215 -1.665037 1.376869 +v -3.426879 -1.607742 1.376869 +v -3.426879 -1.907742 1.076869 +v -3.426879 -1.907742 1.271261 +vt 0.252891 0.122706 +vt 0.283344 0.107201 +vt 0.298868 0.076756 +vt 0.293532 0.043002 +vt 0.269375 0.018830 +vt 0.235623 0.013474 +vt 0.205170 0.028980 +vt 0.189646 0.059424 +vt 0.194982 0.093179 +vt 0.219140 0.117351 +vt 0.987153 0.141536 +vt 0.986865 0.156219 +vt 0.986578 0.170901 +vt 0.986290 0.185584 +vt 0.986003 0.200266 +vt 0.985715 0.214949 +vt 0.985428 0.229631 +vt 0.985140 0.244313 +vt 0.984853 0.258995 +vt 0.984565 0.273678 +vt 0.984278 0.288360 +vt 0.766365 0.137213 +vt 0.766077 0.151896 +vt 0.765790 0.166578 +vt 0.765503 0.181260 +vt 0.765215 0.195943 +vt 0.764927 0.210625 +vt 0.764640 0.225307 +vt 0.764352 0.239990 +vt 0.764065 0.254672 +vt 0.763777 0.269355 +vt 0.763490 0.284037 +vt 0.498215 0.317674 +vt 0.521155 0.305994 +vt 0.532848 0.283062 +vt 0.528828 0.257636 +vt 0.510632 0.239429 +vt 0.485208 0.235394 +vt 0.462269 0.247074 +vt 0.450576 0.270006 +vt 0.454595 0.295432 +vt 0.472792 0.313640 +vt 0.246228 0.069095 +vt 0.493196 0.277291 +vn -0.545745 -0.751153 0.371390 +vn -0.883034 -0.286915 0.371390 +vn -0.859567 -0.279290 -0.427951 +vn -0.531241 -0.731192 -0.427951 +vn -0.883034 0.286915 0.371390 +vn -0.859567 0.279290 -0.427952 +vn -0.545745 0.751154 0.371390 +vn -0.531240 0.731191 -0.427952 +vn 0.000000 0.928477 0.371391 +vn -0.000000 0.903802 -0.427952 +vn 0.545745 0.751153 0.371392 +vn 0.531242 0.731191 -0.427951 +vn 0.883034 0.286914 0.371391 +vn 0.859567 0.279290 -0.427951 +vn 0.883034 -0.286915 0.371391 +vn 0.859567 -0.279290 -0.427951 +vn 0.545745 -0.751154 0.371391 +vn 0.531242 -0.731191 -0.427950 +vn 0.000000 -0.928477 0.371391 +vn -0.000000 -0.903802 -0.427951 +vn -0.000000 0.000000 1.000000 +vn 0.000000 0.000000 -1.000000 +s 1 +f 347/493/818 348/494/819 358/505/820 357/504/821 +f 348/494/819 349/495/822 359/506/823 358/505/820 +f 349/495/822 350/496/824 360/507/825 359/506/823 +f 350/496/824 351/497/826 361/508/827 360/507/825 +f 351/497/826 352/498/828 362/509/829 361/508/827 +f 352/498/828 353/499/830 363/510/831 362/509/829 +f 353/499/830 354/500/832 364/511/833 363/510/831 +f 354/500/832 355/501/834 365/512/835 364/511/833 +f 355/501/834 356/502/836 366/513/837 365/512/835 +f 356/502/836 347/503/818 357/514/821 366/513/837 +f 348/484/819 347/483/818 367/525/838 +f 349/485/822 348/484/819 367/525/838 +f 350/486/824 349/485/822 367/525/838 +f 351/487/826 350/486/824 367/525/838 +f 352/488/828 351/487/826 367/525/838 +f 353/489/830 352/488/828 367/525/838 +f 354/490/832 353/489/830 367/525/838 +f 355/491/834 354/490/832 367/525/838 +f 356/492/836 355/491/834 367/525/838 +f 347/483/818 356/492/836 367/525/838 +f 357/523/821 358/522/820 368/526/839 +f 358/522/820 359/521/823 368/526/839 +f 359/521/823 360/520/825 368/526/839 +f 360/520/825 361/519/827 368/526/839 +f 361/519/827 362/518/829 368/526/839 +f 362/518/829 363/517/831 368/526/839 +f 363/517/831 364/516/833 368/526/839 +f 364/516/833 365/515/835 368/526/839 +f 365/515/835 366/524/837 368/526/839 +f 366/524/837 357/523/821 368/526/839 +v 3.068942 -0.472892 0.150000 +v 3.068942 -0.658302 0.150000 +v 2.959961 -0.808302 0.150000 +v 2.783625 -0.865597 0.150000 +v 2.607290 -0.808302 0.150000 +v 2.498308 -0.658302 0.150000 +v 2.498308 -0.472892 0.150000 +v 3.068942 -0.472892 -0.150000 +v 3.068942 -0.658302 -0.150000 +v 2.959961 -0.808302 -0.150000 +v 2.783625 -0.865597 -0.150000 +v 2.607290 -0.808302 -0.150000 +v 2.498308 -0.658302 -0.150000 +v 2.498308 -0.472892 -0.150000 +v 2.783625 -0.565597 0.150000 +v 2.783625 -0.565597 -0.044391 +vt 0.283344 0.107201 +vt 0.298868 0.076756 +vt 0.293532 0.043002 +vt 0.269375 0.018830 +vt 0.235623 0.013474 +vt 0.205170 0.028980 +vt 0.189646 0.059424 +vt 0.986865 0.156219 +vt 0.986578 0.170901 +vt 0.986290 0.185584 +vt 0.986003 0.200266 +vt 0.985715 0.214949 +vt 0.985428 0.229631 +vt 0.985140 0.244313 +vt 0.766077 0.151896 +vt 0.765790 0.166578 +vt 0.765503 0.181260 +vt 0.765215 0.195943 +vt 0.764927 0.210625 +vt 0.764640 0.225307 +vt 0.764352 0.239990 +vt 0.521155 0.305994 +vt 0.532848 0.283062 +vt 0.528828 0.257636 +vt 0.510632 0.239429 +vt 0.485208 0.235394 +vt 0.462269 0.247074 +vt 0.450576 0.270006 +vt 0.246228 0.069095 +vt 0.493196 0.277291 +vn 0.934652 0.000000 0.355563 +vn 0.883034 -0.286915 0.371391 +vn 0.859567 -0.279290 -0.427951 +vn 0.911810 0.000000 -0.410612 +vn 0.545745 -0.751153 0.371391 +vn 0.531242 -0.731191 -0.427951 +vn -0.000000 -0.928477 0.371391 +vn -0.000000 -0.903802 -0.427951 +vn -0.545745 -0.751154 0.371391 +vn -0.531241 -0.731191 -0.427951 +vn -0.883034 -0.286915 0.371391 +vn -0.859567 -0.279291 -0.427951 +vn -0.934652 0.000000 0.355563 +vn -0.911810 0.000000 -0.410612 +vn 0.000000 0.000000 1.000000 +vn 0.000000 0.186532 -0.982449 +s 1 +f 369/534/840 370/535/841 377/542/842 376/541/843 +f 370/535/841 371/536/844 378/543/845 377/542/842 +f 371/536/844 372/537/846 379/544/847 378/543/845 +f 372/537/846 373/538/848 380/545/849 379/544/847 +f 373/538/848 374/539/850 381/546/851 380/545/849 +f 374/539/850 375/540/852 382/547/853 381/546/851 +f 370/528/841 369/527/840 383/555/854 +f 371/529/844 370/528/841 383/555/854 +f 372/530/846 371/529/844 383/555/854 +f 373/531/848 372/530/846 383/555/854 +f 374/532/850 373/531/848 383/555/854 +f 375/533/852 374/532/850 383/555/854 +f 376/554/843 377/553/842 384/556/855 +f 377/553/842 378/552/845 384/556/855 +f 378/552/845 379/551/847 384/556/855 +f 379/551/847 380/550/849 384/556/855 +f 380/550/849 381/549/851 384/556/855 +f 381/549/851 382/548/853 384/556/855 diff --git a/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/data/diffuse_512.png b/examples/MPU6050_DMP6_using_DMP_V6.12/MPUplane/data/diffuse_512.png new file mode 100644 index 0000000000000000000000000000000000000000..930d910b947a7e494fc5430018e4f2dc33344fee GIT binary patch literal 361095 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

U5qFeB;E@mN(p95UK+~c+orTlU99_FAxn?B(Q{ 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%0D2-ixWH}v0<%Rx_c@>-~UASg~gI~q6F4I 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

bYExGB<> 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 literal 0 HcmV?d00001 diff --git a/examples/MPU6050_raw/MPU6050_raw.ino b/examples/MPU6050_raw/MPU6050_raw.ino new file mode 100644 index 0000000..596f075 --- /dev/null +++ b/examples/MPU6050_raw/MPU6050_raw.ino @@ -0,0 +1,153 @@ +// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class +// 10/7/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2013-05-08 - added multiple output formats +// - added seamless Fastwire support +// 2011-10-07 - initial release + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2011 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files +// for both classes must be in the include path of your project +#include "I2Cdev.h" +#include "MPU6050.h" + +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation +// is used in I2Cdev.h +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + #include "Wire.h" +#endif + +// class default I2C address is 0x68 +// specific I2C addresses may be passed as a parameter here +// AD0 low = 0x68 (default for InvenSense evaluation board) +// AD0 high = 0x69 +MPU6050 accelgyro; +//MPU6050 accelgyro(0x69); // <-- use for AD0 high +//MPU6050 accelgyro(0x68, &Wire1); // <-- use for AD0 low, but 2nd Wire (TWI/I2C) object + +int16_t ax, ay, az; +int16_t gx, gy, gz; + + + +// uncomment "OUTPUT_READABLE_ACCELGYRO" if you want to see a tab-separated +// list of the accel X/Y/Z and then gyro X/Y/Z values in decimal. Easy to read, +// not so easy to parse, and slow(er) over UART. +#define OUTPUT_READABLE_ACCELGYRO + +// uncomment "OUTPUT_BINARY_ACCELGYRO" to send all 6 axes of data as 16-bit +// binary, one right after the other. This is very fast (as fast as possible +// without compression or data loss), and easy to parse, but impossible to read +// for a human. +//#define OUTPUT_BINARY_ACCELGYRO + + +#define LED_PIN 13 +bool blinkState = false; + +void setup() { + // join I2C bus (I2Cdev library doesn't do this automatically) + #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + Wire.begin(); + #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE + Fastwire::setup(400, true); + #endif + + // initialize serial communication + // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but + // it's really up to you depending on your project) + Serial.begin(38400); + + // initialize device + Serial.println("Initializing I2C devices..."); + accelgyro.initialize(); + + // verify connection + Serial.println("Testing device connections..."); + Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed"); + + // use the code below to change accel/gyro offset values + /* + Serial.println("Updating internal sensor offsets..."); + // -76 -2359 1688 0 0 0 + Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76 + Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359 + Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688 + Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0 + Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0 + Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0 + Serial.print("\n"); + accelgyro.setXGyroOffset(220); + accelgyro.setYGyroOffset(76); + accelgyro.setZGyroOffset(-85); + Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76 + Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359 + Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688 + Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0 + Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0 + Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0 + Serial.print("\n"); + */ + + // configure Arduino LED pin for output + pinMode(LED_PIN, OUTPUT); +} + +void loop() { + // read raw accel/gyro measurements from device + accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); + + // these methods (and a few others) are also available + //accelgyro.getAcceleration(&ax, &ay, &az); + //accelgyro.getRotation(&gx, &gy, &gz); + + #ifdef OUTPUT_READABLE_ACCELGYRO + // display tab-separated accel/gyro x/y/z values + Serial.print("a/g:\t"); + Serial.print(ax); Serial.print("\t"); + Serial.print(ay); Serial.print("\t"); + Serial.print(az); Serial.print("\t"); + Serial.print(gx); Serial.print("\t"); + Serial.print(gy); Serial.print("\t"); + Serial.println(gz); + #endif + + #ifdef OUTPUT_BINARY_ACCELGYRO + Serial.write((uint8_t)(ax >> 8)); Serial.write((uint8_t)(ax & 0xFF)); + Serial.write((uint8_t)(ay >> 8)); Serial.write((uint8_t)(ay & 0xFF)); + Serial.write((uint8_t)(az >> 8)); Serial.write((uint8_t)(az & 0xFF)); + Serial.write((uint8_t)(gx >> 8)); Serial.write((uint8_t)(gx & 0xFF)); + Serial.write((uint8_t)(gy >> 8)); Serial.write((uint8_t)(gy & 0xFF)); + Serial.write((uint8_t)(gz >> 8)); Serial.write((uint8_t)(gz & 0xFF)); + #endif + + // blink LED to indicate activity + blinkState = !blinkState; + digitalWrite(LED_PIN, blinkState); + delay(100); +} diff --git a/helper_3dmath.h b/helper_3dmath.h new file mode 100644 index 0000000..9ed260e --- /dev/null +++ b/helper_3dmath.h @@ -0,0 +1,216 @@ +// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class, 3D math helper +// 6/5/2012 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2012-06-05 - add 3D math helper file to DMP6 example sketch + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#ifndef _HELPER_3DMATH_H_ +#define _HELPER_3DMATH_H_ + +class Quaternion { + public: + float w; + float x; + float y; + float z; + + Quaternion() { + w = 1.0f; + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + Quaternion(float nw, float nx, float ny, float nz) { + w = nw; + x = nx; + y = ny; + z = nz; + } + + Quaternion getProduct(Quaternion q) { + // Quaternion multiplication is defined by: + // (Q1 * Q2).w = (w1w2 - x1x2 - y1y2 - z1z2) + // (Q1 * Q2).x = (w1x2 + x1w2 + y1z2 - z1y2) + // (Q1 * Q2).y = (w1y2 - x1z2 + y1w2 + z1x2) + // (Q1 * Q2).z = (w1z2 + x1y2 - y1x2 + z1w2 + return Quaternion( + w*q.w - x*q.x - y*q.y - z*q.z, // new w + w*q.x + x*q.w + y*q.z - z*q.y, // new x + w*q.y - x*q.z + y*q.w + z*q.x, // new y + w*q.z + x*q.y - y*q.x + z*q.w); // new z + } + + Quaternion getConjugate() { + return Quaternion(w, -x, -y, -z); + } + + float getMagnitude() { + return sqrt(w*w + x*x + y*y + z*z); + } + + void normalize() { + float m = getMagnitude(); + w /= m; + x /= m; + y /= m; + z /= m; + } + + Quaternion getNormalized() { + Quaternion r(w, x, y, z); + r.normalize(); + return r; + } +}; + +class VectorInt16 { + public: + int16_t x; + int16_t y; + int16_t z; + + VectorInt16() { + x = 0; + y = 0; + z = 0; + } + + VectorInt16(int16_t nx, int16_t ny, int16_t nz) { + x = nx; + y = ny; + z = nz; + } + + float getMagnitude() { + return sqrt(x*x + y*y + z*z); + } + + void normalize() { + float m = getMagnitude(); + x /= m; + y /= m; + z /= m; + } + + VectorInt16 getNormalized() { + VectorInt16 r(x, y, z); + r.normalize(); + return r; + } + + void rotate(Quaternion *q) { + // http://www.cprogramming.com/tutorial/3d/quaternions.html + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/index.htm + // http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation + // ^ or: http://webcache.googleusercontent.com/search?q=cache:xgJAp3bDNhQJ:content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation&hl=en&gl=us&strip=1 + + // P_out = q * P_in * conj(q) + // - P_out is the output vector + // - q is the orientation quaternion + // - P_in is the input vector (a*aReal) + // - conj(q) is the conjugate of the orientation quaternion (q=[w,x,y,z], q*=[w,-x,-y,-z]) + Quaternion p(0, x, y, z); + + // quaternion multiplication: q * p, stored back in p + p = q -> getProduct(p); + + // quaternion multiplication: p * conj(q), stored back in p + p = p.getProduct(q -> getConjugate()); + + // p quaternion is now [0, x', y', z'] + x = p.x; + y = p.y; + z = p.z; + } + + VectorInt16 getRotated(Quaternion *q) { + VectorInt16 r(x, y, z); + r.rotate(q); + return r; + } +}; + +class VectorFloat { + public: + float x; + float y; + float z; + + VectorFloat() { + x = 0; + y = 0; + z = 0; + } + + VectorFloat(float nx, float ny, float nz) { + x = nx; + y = ny; + z = nz; + } + + float getMagnitude() { + return sqrt(x*x + y*y + z*z); + } + + void normalize() { + float m = getMagnitude(); + x /= m; + y /= m; + z /= m; + } + + VectorFloat getNormalized() { + VectorFloat r(x, y, z); + r.normalize(); + return r; + } + + void rotate(Quaternion *q) { + Quaternion p(0, x, y, z); + + // quaternion multiplication: q * p, stored back in p + p = q -> getProduct(p); + + // quaternion multiplication: p * conj(q), stored back in p + p = p.getProduct(q -> getConjugate()); + + // p quaternion is now [0, x', y', z'] + x = p.x; + y = p.y; + z = p.z; + } + + VectorFloat getRotated(Quaternion *q) { + VectorFloat r(x, y, z); + r.rotate(q); + return r; + } +}; + +#endif /* _HELPER_3DMATH_H_ */ \ No newline at end of file diff --git a/library.json b/library.json new file mode 100644 index 0000000..3b5ad90 --- /dev/null +++ b/library.json @@ -0,0 +1,18 @@ +{ + "name": "I2Cdevlib-MPU6050", + "version": "1.0.0", + "keywords": "gyroscope, accelerometer, sensor, i2cdevlib, i2c", + "description": "The MPU6050 combines a 3-axis gyroscope and a 3-axis accelerometer on the same silicon die together with an onboard Digital Motion Processor(DMP) which processes complex 6-axis MotionFusion algorithms", + "include": "Arduino/MPU6050", + "repository": + { + "type": "git", + "url": "https://github.com/jrowberg/i2cdevlib.git" + }, + "dependencies": + { + "jrowberg/I2Cdevlib-Core": "*" + }, + "frameworks": "arduino", + "platforms": "*" +}