|
| 1 | +/* ============================================ |
| 2 | +I2Cdev device library code is placed under the MIT license |
| 3 | +Copyright (c) 2012 Jeff Rowberg |
| 4 | +
|
| 5 | +Permission is hereby granted, free of charge, to any person obtaining a copy |
| 6 | +of this software and associated documentation files (the "Software"), to deal |
| 7 | +in the Software without restriction, including without limitation the rights |
| 8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 9 | +copies of the Software, and to permit persons to whom the Software is |
| 10 | +furnished to do so, subject to the following conditions: |
| 11 | +
|
| 12 | +The above copyright notice and this permission notice shall be included in |
| 13 | +all copies or substantial portions of the Software. |
| 14 | +
|
| 15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 21 | +THE SOFTWARE. |
| 22 | +=============================================== |
| 23 | +*/ |
| 24 | + |
| 25 | +/* This driver reads quaternion data from the MPU6060 and sends |
| 26 | + Open Sound Control messages. |
| 27 | +
|
| 28 | + GY-521 NodeMCU |
| 29 | + MPU6050 devkit 1.0 |
| 30 | + board Lolin Description |
| 31 | + ======= ========== ==================================================== |
| 32 | + VCC VU (5V USB) Not available on all boards so use 3.3V if needed. |
| 33 | + GND G Ground |
| 34 | + SCL D1 (GPIO05) I2C clock |
| 35 | + SDA D2 (GPIO04) I2C data |
| 36 | + XDA not connected |
| 37 | + XCL not connected |
| 38 | + AD0 not connected |
| 39 | + INT D8 (GPIO15) Interrupt pin |
| 40 | +
|
| 41 | +*/ |
| 42 | + |
| 43 | +#if defined(ESP8266) |
| 44 | +#include <ESP8266WiFi.h> |
| 45 | +#else |
| 46 | +#include <WiFi.h> |
| 47 | +#endif |
| 48 | +#include <DNSServer.h> |
| 49 | +#include <WiFiClient.h> |
| 50 | +#include <WiFiUdp.h> |
| 51 | +#include <OSCMessage.h> |
| 52 | +#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager |
| 53 | + |
| 54 | +// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files |
| 55 | +// for both classes must be in the include path of your project |
| 56 | +#include "I2Cdev.h" |
| 57 | + |
| 58 | +#include "MPU6050_6Axis_MotionApps20.h" |
| 59 | +//#include "MPU6050.h" // not necessary if using MotionApps include file |
| 60 | + |
| 61 | +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation |
| 62 | +// is used in I2Cdev.h |
| 63 | +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE |
| 64 | + #include "Wire.h" |
| 65 | +#endif |
| 66 | + |
| 67 | +// class default I2C address is 0x68 |
| 68 | +// specific I2C addresses may be passed as a parameter here |
| 69 | +// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board) |
| 70 | +// AD0 high = 0x69 |
| 71 | +MPU6050 mpu; |
| 72 | +//MPU6050 mpu(0x69); // <-- use for AD0 high |
| 73 | + |
| 74 | +/* ========================================================================= |
| 75 | + NOTE: In addition to connection 5/3.3v, GND, SDA, and SCL, this sketch |
| 76 | + depends on the MPU-6050's INT pin being connected to the ESP8266 GPIO15 |
| 77 | + pin. |
| 78 | + * ========================================================================= */ |
| 79 | + |
| 80 | +// MPU control/status vars |
| 81 | +bool dmpReady = false; // set true if DMP init was successful |
| 82 | +uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU |
| 83 | +uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) |
| 84 | +uint16_t packetSize; // expected DMP packet size (default is 42 bytes) |
| 85 | +uint16_t fifoCount; // count of all bytes currently in FIFO |
| 86 | +uint8_t fifoBuffer[64]; // FIFO storage buffer |
| 87 | + |
| 88 | +// orientation/motion vars |
| 89 | +Quaternion q; // [w, x, y, z] quaternion container |
| 90 | +VectorInt16 aa; // [x, y, z] accel sensor measurements |
| 91 | +VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements |
| 92 | +VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements |
| 93 | +VectorFloat gravity; // [x, y, z] gravity vector |
| 94 | +#ifdef OUTPUT_READABLE_EULER |
| 95 | +float euler[3]; // [psi, theta, phi] Euler angle container |
| 96 | +#endif |
| 97 | +#ifdef OUTPUT_READABLE_YAWPITCHROLL |
| 98 | +float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector |
| 99 | +#endif |
| 100 | + |
| 101 | +// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual |
| 102 | +// quaternion components in a [w, x, y, z] format (not best for parsing |
| 103 | +// on a remote host such as Processing or something though) |
| 104 | +//#define OUTPUT_READABLE_QUATERNION |
| 105 | + |
| 106 | +// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles |
| 107 | +// (in degrees) calculated from the quaternions coming from the FIFO. |
| 108 | +// Note that Euler angles suffer from gimbal lock (for more info, see |
| 109 | +// http://en.wikipedia.org/wiki/Gimbal_lock) |
| 110 | +//#define OUTPUT_READABLE_EULER |
| 111 | + |
| 112 | +// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/ |
| 113 | +// pitch/roll angles (in degrees) calculated from the quaternions coming |
| 114 | +// from the FIFO. Note this also requires gravity vector calculations. |
| 115 | +// Also note that yaw/pitch/roll angles suffer from gimbal lock (for |
| 116 | +// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock) |
| 117 | +//#define OUTPUT_READABLE_YAWPITCHROLL |
| 118 | + |
| 119 | +// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration |
| 120 | +// components with gravity removed. This acceleration reference frame is |
| 121 | +// not compensated for orientation, so +X is always +X according to the |
| 122 | +// sensor, just without the effects of gravity. If you want acceleration |
| 123 | +// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead. |
| 124 | +//#define OUTPUT_READABLE_REALACCEL |
| 125 | + |
| 126 | +// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration |
| 127 | +// components with gravity removed and adjusted for the world frame of |
| 128 | +// reference (yaw is relative to initial orientation, since no magnetometer |
| 129 | +// is present in this case). Could be quite handy in some cases. |
| 130 | +//#define OUTPUT_READABLE_WORLDACCEL |
| 131 | + |
| 132 | +// uncomment "OUTPUT_TEAPOT_OSC" if you want output that matches the |
| 133 | +// format used for the InvenSense teapot demo |
| 134 | +#define OUTPUT_TEAPOT_OSC |
| 135 | + |
| 136 | +#define INTERRUPT_PIN 15 // use pin 15 on ESP8266 |
| 137 | + |
| 138 | +const char DEVICE_NAME[] = "mpu6050"; |
| 139 | + |
| 140 | +WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP |
| 141 | +const IPAddress outIp(192, 168, 1, 11); // remote IP to receive OSC |
| 142 | +const unsigned int outPort = 9999; // remote port to receive OSC |
| 143 | + |
| 144 | +// ================================================================ |
| 145 | +// === INTERRUPT DETECTION ROUTINE === |
| 146 | +// ================================================================ |
| 147 | + |
| 148 | +volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high |
| 149 | +void dmpDataReady() { |
| 150 | + mpuInterrupt = true; |
| 151 | +} |
| 152 | + |
| 153 | +void mpu_setup() |
| 154 | +{ |
| 155 | + // join I2C bus (I2Cdev library doesn't do this automatically) |
| 156 | +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE |
| 157 | + Wire.begin(); |
| 158 | + Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties |
| 159 | +#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE |
| 160 | + Fastwire::setup(400, true); |
| 161 | +#endif |
| 162 | + |
| 163 | + // initialize device |
| 164 | + Serial.println(F("Initializing I2C devices...")); |
| 165 | + mpu.initialize(); |
| 166 | + pinMode(INTERRUPT_PIN, INPUT); |
| 167 | + |
| 168 | + // verify connection |
| 169 | + Serial.println(F("Testing device connections...")); |
| 170 | + Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); |
| 171 | + |
| 172 | + // load and configure the DMP |
| 173 | + Serial.println(F("Initializing DMP...")); |
| 174 | + devStatus = mpu.dmpInitialize(); |
| 175 | + |
| 176 | + // supply your own gyro offsets here, scaled for min sensitivity |
| 177 | + mpu.setXGyroOffset(220); |
| 178 | + mpu.setYGyroOffset(76); |
| 179 | + mpu.setZGyroOffset(-85); |
| 180 | + mpu.setZAccelOffset(1788); // 1688 factory default for my test chip |
| 181 | + |
| 182 | + // make sure it worked (returns 0 if so) |
| 183 | + if (devStatus == 0) { |
| 184 | + // turn on the DMP, now that it's ready |
| 185 | + Serial.println(F("Enabling DMP...")); |
| 186 | + mpu.setDMPEnabled(true); |
| 187 | + |
| 188 | + // enable Arduino interrupt detection |
| 189 | + Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)...")); |
| 190 | + attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); |
| 191 | + mpuIntStatus = mpu.getIntStatus(); |
| 192 | + |
| 193 | + // set our DMP Ready flag so the main loop() function knows it's okay to use it |
| 194 | + Serial.println(F("DMP ready! Waiting for first interrupt...")); |
| 195 | + dmpReady = true; |
| 196 | + |
| 197 | + // get expected DMP packet size for later comparison |
| 198 | + packetSize = mpu.dmpGetFIFOPacketSize(); |
| 199 | + } else { |
| 200 | + // ERROR! |
| 201 | + // 1 = initial memory load failed |
| 202 | + // 2 = DMP configuration updates failed |
| 203 | + // (if it's going to break, usually the code will be 1) |
| 204 | + Serial.print(F("DMP Initialization failed (code ")); |
| 205 | + Serial.print(devStatus); |
| 206 | + Serial.println(F(")")); |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +void setup(void) |
| 211 | +{ |
| 212 | + Serial.begin(115200); |
| 213 | + Serial.println(F("\nOrientation Sensor OSC output")); Serial.println(); |
| 214 | + |
| 215 | + //WiFiManager |
| 216 | + //Local intialization. Once its business is done, there is no need to keep it around |
| 217 | + WiFiManager wifiManager; |
| 218 | + //reset saved settings |
| 219 | + //wifiManager.resetSettings(); |
| 220 | + |
| 221 | + //fetches ssid and pass from eeprom and tries to connect |
| 222 | + //if it does not connect it starts an access point with the specified name |
| 223 | + //and goes into a blocking loop awaiting configuration |
| 224 | + wifiManager.autoConnect(DEVICE_NAME); |
| 225 | + |
| 226 | + Serial.print(F("WiFi connected! IP address: ")); |
| 227 | + Serial.println(WiFi.localIP()); |
| 228 | + |
| 229 | + mpu_setup(); |
| 230 | +} |
| 231 | + |
| 232 | +void mpu_loop() |
| 233 | +{ |
| 234 | + // if programming failed, don't try to do anything |
| 235 | + if (!dmpReady) return; |
| 236 | + |
| 237 | + // wait for MPU interrupt or extra packet(s) available |
| 238 | + if (!mpuInterrupt && fifoCount < packetSize) return; |
| 239 | + |
| 240 | + // reset interrupt flag and get INT_STATUS byte |
| 241 | + mpuInterrupt = false; |
| 242 | + mpuIntStatus = mpu.getIntStatus(); |
| 243 | + |
| 244 | + // get current FIFO count |
| 245 | + fifoCount = mpu.getFIFOCount(); |
| 246 | + |
| 247 | + // check for overflow (this should never happen unless our code is too inefficient) |
| 248 | + if ((mpuIntStatus & 0x10) || fifoCount == 1024) { |
| 249 | + // reset so we can continue cleanly |
| 250 | + mpu.resetFIFO(); |
| 251 | + Serial.println(F("FIFO overflow!")); |
| 252 | + |
| 253 | + // otherwise, check for DMP data ready interrupt (this should happen frequently) |
| 254 | + } else if (mpuIntStatus & 0x02) { |
| 255 | + // wait for correct available data length, should be a VERY short wait |
| 256 | + while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); |
| 257 | + |
| 258 | + // read a packet from FIFO |
| 259 | + mpu.getFIFOBytes(fifoBuffer, packetSize); |
| 260 | + |
| 261 | + // track FIFO count here in case there is > 1 packet available |
| 262 | + // (this lets us immediately read more without waiting for an interrupt) |
| 263 | + fifoCount -= packetSize; |
| 264 | + |
| 265 | +#ifdef OUTPUT_READABLE_QUATERNION |
| 266 | + // display quaternion values in easy matrix form: w x y z |
| 267 | + mpu.dmpGetQuaternion(&q, fifoBuffer); |
| 268 | + Serial.print("quat\t"); |
| 269 | + Serial.print(q.w); |
| 270 | + Serial.print("\t"); |
| 271 | + Serial.print(q.x); |
| 272 | + Serial.print("\t"); |
| 273 | + Serial.print(q.y); |
| 274 | + Serial.print("\t"); |
| 275 | + Serial.println(q.z); |
| 276 | +#endif |
| 277 | + |
| 278 | +#ifdef OUTPUT_TEAPOT_OSC |
| 279 | +#ifndef OUTPUT_READABLE_QUATERNION |
| 280 | + // display quaternion values in easy matrix form: w x y z |
| 281 | + mpu.dmpGetQuaternion(&q, fifoBuffer); |
| 282 | +#endif |
| 283 | + // Send OSC message |
| 284 | + OSCMessage msg("/imuquat"); |
| 285 | + msg.add((float)q.w); |
| 286 | + msg.add((float)q.x); |
| 287 | + msg.add((float)q.y); |
| 288 | + msg.add((float)q.z); |
| 289 | + |
| 290 | + Udp.beginPacket(outIp, outPort); |
| 291 | + msg.send(Udp); |
| 292 | + Udp.endPacket(); |
| 293 | + |
| 294 | + msg.empty(); |
| 295 | +#endif |
| 296 | + |
| 297 | +#ifdef OUTPUT_READABLE_EULER |
| 298 | + // display Euler angles in degrees |
| 299 | + mpu.dmpGetQuaternion(&q, fifoBuffer); |
| 300 | + mpu.dmpGetEuler(euler, &q); |
| 301 | + Serial.print("euler\t"); |
| 302 | + Serial.print(euler[0] * 180/M_PI); |
| 303 | + Serial.print("\t"); |
| 304 | + Serial.print(euler[1] * 180/M_PI); |
| 305 | + Serial.print("\t"); |
| 306 | + Serial.println(euler[2] * 180/M_PI); |
| 307 | +#endif |
| 308 | + |
| 309 | +#ifdef OUTPUT_READABLE_YAWPITCHROLL |
| 310 | + // display Euler angles in degrees |
| 311 | + mpu.dmpGetQuaternion(&q, fifoBuffer); |
| 312 | + mpu.dmpGetGravity(&gravity, &q); |
| 313 | + mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); |
| 314 | + Serial.print("ypr\t"); |
| 315 | + Serial.print(ypr[0] * 180/M_PI); |
| 316 | + Serial.print("\t"); |
| 317 | + Serial.print(ypr[1] * 180/M_PI); |
| 318 | + Serial.print("\t"); |
| 319 | + Serial.println(ypr[2] * 180/M_PI); |
| 320 | +#endif |
| 321 | + |
| 322 | +#ifdef OUTPUT_READABLE_REALACCEL |
| 323 | + // display real acceleration, adjusted to remove gravity |
| 324 | + mpu.dmpGetQuaternion(&q, fifoBuffer); |
| 325 | + mpu.dmpGetAccel(&aa, fifoBuffer); |
| 326 | + mpu.dmpGetGravity(&gravity, &q); |
| 327 | + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); |
| 328 | + Serial.print("areal\t"); |
| 329 | + Serial.print(aaReal.x); |
| 330 | + Serial.print("\t"); |
| 331 | + Serial.print(aaReal.y); |
| 332 | + Serial.print("\t"); |
| 333 | + Serial.println(aaReal.z); |
| 334 | +#endif |
| 335 | + |
| 336 | +#ifdef OUTPUT_READABLE_WORLDACCEL |
| 337 | + // display initial world-frame acceleration, adjusted to remove gravity |
| 338 | + // and rotated based on known orientation from quaternion |
| 339 | + mpu.dmpGetQuaternion(&q, fifoBuffer); |
| 340 | + mpu.dmpGetAccel(&aa, fifoBuffer); |
| 341 | + mpu.dmpGetGravity(&gravity, &q); |
| 342 | + mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); |
| 343 | + mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); |
| 344 | + Serial.print("aworld\t"); |
| 345 | + Serial.print(aaWorld.x); |
| 346 | + Serial.print("\t"); |
| 347 | + Serial.print(aaWorld.y); |
| 348 | + Serial.print("\t"); |
| 349 | + Serial.println(aaWorld.z); |
| 350 | +#endif |
| 351 | + } |
| 352 | +} |
| 353 | + |
| 354 | +/**************************************************************************/ |
| 355 | +/* |
| 356 | + Arduino loop function, called once 'setup' is complete (your own code |
| 357 | + should go here) |
| 358 | +*/ |
| 359 | +/**************************************************************************/ |
| 360 | +void loop(void) |
| 361 | +{ |
| 362 | + if (WiFi.status() != WL_CONNECTED) { |
| 363 | + Serial.println(); |
| 364 | + Serial.println("*** Disconnected from AP so rebooting ***"); |
| 365 | + Serial.println(); |
| 366 | + ESP.reset(); |
| 367 | + } |
| 368 | + |
| 369 | + mpu_loop(); |
| 370 | +} |
0 commit comments