IMO support in usermod games, add 3D IMO cube effect
- platformio: add USERMOD_MPU6050_IMU and ElectronicCats/MPU6050 @ 0.6.0 to esp32mdevums - USERMOD_MPU6050_IMU: update readme.md and usermod_mpu650_imu.h - usermod_v2_games: support for USERMOD_MPU6050_IMU, add mode_IMUTest and class Frame3D and mode_3DIMUCube, remove old gyro handling - usermods_list.cpp: add USERMOD_MPU6050_IMU
This commit is contained in:
@@ -20,9 +20,9 @@ react to the globes orientation. See the blog post on building it <https://www.r
|
||||
|
||||
I2Cdev and MPU6050 must be installed.
|
||||
|
||||
To install them, add I2Cdevlib-MPU6050@fbde122cc5 to lib_deps in the platformio.ini file.
|
||||
To install them, add ElectronicCats/MPU6050 @ 0.6.0 to lib_deps in the platformio.ini file.
|
||||
|
||||
You also need to change lib_compat_mode from strict to soft in platformio.ini (This ignores that I2Cdevlib-MPU6050 doesn't list platform compatibility)
|
||||
<!-- You also need to change lib_compat_mode from strict to soft in platformio.ini (This ignores that I2Cdevlib-MPU6050 doesn't list platform compatibility) -->
|
||||
|
||||
For example:
|
||||
|
||||
@@ -36,7 +36,7 @@ lib_deps =
|
||||
AsyncTCP@1.0.3
|
||||
Esp Async WebServer@1.2.0
|
||||
IRremoteESP8266@2.7.3
|
||||
I2Cdevlib-MPU6050@fbde122cc5
|
||||
ElectronicCats/MPU6050 @ 0.6.0
|
||||
```
|
||||
|
||||
## Wiring
|
||||
@@ -77,18 +77,19 @@ to the info object
|
||||
|
||||
## Usermod installation
|
||||
|
||||
1. Copy the file `usermod_mpu6050_imu.h` to the `wled00` directory.
|
||||
2. Register the usermod by adding `#include "usermod_mpu6050_imu.h.h"` in the top and `registerUsermod(new MPU6050Driver());` in the bottom of `usermods_list.cpp`.
|
||||
<!-- 1. Copy the file `usermod_mpu6050_imu.h` to the `wled00` directory. -->
|
||||
2. Register the usermod by adding `#include "usermod_mpu6050_imu.h"` in the top and `registerUsermod(new MPU6050Driver());` in the bottom of `usermods_list.cpp`.
|
||||
|
||||
Example **usermods_list.cpp**:
|
||||
|
||||
```cpp
|
||||
#include "wled.h"
|
||||
|
||||
#include "usermod_mpu6050_imu.h"
|
||||
#ifdef USERMOD_MPU6050_IMU
|
||||
#include "../usermods/mpu6050_imu/usermod_mpu6050_imu.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
#ifdef USERMOD_MPU6050_IMU
|
||||
usermods.add(new MPU6050Driver());
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#define DEBUG_PRINT_IMU(x) Serial.print(x)
|
||||
#define DEBUG_PRINT_IMULN(x) Serial.println(x)
|
||||
#define DEBUG_PRINT_IMUF(x...) Serial.printf(x)
|
||||
#else
|
||||
#define DEBUG_PRINT_IMU(x)
|
||||
#define DEBUG_PRINT_IMULN(x)
|
||||
#define DEBUG_PRINT_IMUF(x...)
|
||||
#endif
|
||||
|
||||
|
||||
/* This driver reads quaternion data from the MPU6060 and adds it to the JSON
|
||||
This example is adapted from:
|
||||
https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050/examples/MPU6050_DMP6_ESPWiFi
|
||||
@@ -26,15 +37,14 @@
|
||||
2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
3. I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h file
|
||||
for both classes must be in the include path of your project. To install the
|
||||
libraries add I2Cdevlib-MPU6050@fbde122cc5 to lib_deps in the platformio.ini file.
|
||||
4. You also need to change lib_compat_mode from strict to soft in platformio.ini (This ignores that I2Cdevlib-MPU6050 doesn't list platform compatibility)
|
||||
libraries add ElectronicCats/MPU6050 @ 0.6.0 to lib_deps in the platformio.ini file.
|
||||
// 4. You also need to change lib_compat_mode from strict to soft in platformio.ini (This ignores that I2Cdevlib-MPU6050 doesn't list platform compatibility)
|
||||
5. Wire up the MPU6050 as detailed above.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -56,6 +66,7 @@ class MPU6050Driver : public Usermod {
|
||||
private:
|
||||
MPU6050 mpu;
|
||||
bool enabled = true;
|
||||
unsigned long lastUMRun = millis();
|
||||
|
||||
// MPU control/status vars
|
||||
bool dmpReady = false; // set true if DMP init was successful
|
||||
@@ -65,47 +76,61 @@ class MPU6050Driver : public Usermod {
|
||||
uint16_t fifoCount; // count of all bytes currently in FIFO
|
||||
uint8_t fifoBuffer[64]; // FIFO storage buffer
|
||||
|
||||
//NOTE: some of these can be removed to save memory, processing time
|
||||
// if the measurement isn't needed
|
||||
public:
|
||||
// orientation/motion vars
|
||||
Quaternion qat; // [w, x, y, z] quaternion container
|
||||
float euler[3]; // [psi, theta, phi] Euler angle container
|
||||
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll 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
|
||||
|
||||
static const int INTERRUPT_PIN = 15; // use pin 15 on ESP8266
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
*/
|
||||
void setup() {
|
||||
DEBUG_PRINT_IMULN("mpu setup");
|
||||
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
|
||||
|
||||
// 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
|
||||
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);
|
||||
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
|
||||
DEBUG_PRINTLN(F("Initializing I2C devices..."));
|
||||
DEBUG_PRINT_IMULN(F("Initializing I2C devices..."));
|
||||
mpu.initialize();
|
||||
pinMode(INTERRUPT_PIN, INPUT);
|
||||
|
||||
// verify connection
|
||||
DEBUG_PRINTLN(F("Testing device connections..."));
|
||||
DEBUG_PRINTLN(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
|
||||
DEBUG_PRINT_IMULN(F("Testing device connections..."));
|
||||
DEBUG_PRINT_IMULN(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
|
||||
|
||||
// // wait for ready
|
||||
// DEBUG_PRINT_IMULN(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
|
||||
DEBUG_PRINTLN(F("Initializing DMP..."));
|
||||
DEBUG_PRINT_IMULN(F("Initializing DMP..."));
|
||||
devStatus = mpu.dmpInitialize();
|
||||
|
||||
// supply your own gyro offsets here, scaled for min sensitivity
|
||||
@@ -116,77 +141,52 @@ class MPU6050Driver : public Usermod {
|
||||
|
||||
// make sure it worked (returns 0 if so)
|
||||
if (devStatus == 0) {
|
||||
// turn on the DMP, now that it's ready
|
||||
DEBUG_PRINTLN(F("Enabling DMP..."));
|
||||
mpu.setDMPEnabled(true);
|
||||
// Calibration Time: generate offsets and calibrate our MPU6050
|
||||
mpu.CalibrateAccel(6);
|
||||
mpu.CalibrateGyro(6);
|
||||
#ifdef WLED_DEBUG
|
||||
mpu.PrintActiveOffsets();
|
||||
#endif
|
||||
// turn on the DMP, now that it's ready
|
||||
DEBUG_PRINT_IMULN(F("Enabling DMP..."));
|
||||
mpu.setDMPEnabled(true);
|
||||
|
||||
// enable Arduino interrupt detection
|
||||
DEBUG_PRINTLN(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
|
||||
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
|
||||
mpuIntStatus = mpu.getIntStatus();
|
||||
// enable Arduino interrupt detection
|
||||
DEBUG_PRINT_IMU(F("Enabling interrupt detection (Arduino external interrupt "));
|
||||
DEBUG_PRINT_IMU(digitalPinToInterrupt(INTERRUPT_PIN));
|
||||
DEBUG_PRINT_IMULN(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
|
||||
DEBUG_PRINTLN(F("DMP ready! Waiting for first interrupt..."));
|
||||
dmpReady = true;
|
||||
// set our DMP Ready flag so the main loop() function knows it's okay to use it
|
||||
DEBUG_PRINT_IMULN(F("DMP ready! Waiting for first interrupt..."));
|
||||
dmpReady = true;
|
||||
|
||||
// get expected DMP packet size for later comparison
|
||||
packetSize = mpu.dmpGetFIFOPacketSize();
|
||||
// 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)
|
||||
DEBUG_PRINT(F("DMP Initialization failed (code "));
|
||||
DEBUG_PRINT(devStatus);
|
||||
DEBUG_PRINTLN(F(")"));
|
||||
// ERROR!
|
||||
// 1 = initial memory load failed
|
||||
// 2 = DMP configuration updates failed
|
||||
// (if it's going to break, usually the code will be 1)
|
||||
DEBUG_PRINT_IMU(F("DMP Initialization failed (code "));
|
||||
DEBUG_PRINT_IMU(devStatus);
|
||||
DEBUG_PRINT_IMULN(F(")"));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected() {
|
||||
//DEBUG_PRINTLN("Connected to WiFi!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop() {
|
||||
// if programming failed, don't try to do anything
|
||||
if (!enabled || !dmpReady || strip.isUpdating()) 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();
|
||||
DEBUG_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;
|
||||
|
||||
if (!enabled || (strip.isUpdating() && (millis() - lastUMRun < 2))) return; // be nice, but not too nice
|
||||
lastUMRun = millis(); // update time keeping
|
||||
|
||||
if (!dmpReady) return;
|
||||
// read a packet from FIFO
|
||||
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet
|
||||
//NOTE: some of these can be removed to save memory, processing time
|
||||
// if the measurement isn't needed
|
||||
mpu.dmpGetQuaternion(&qat, fifoBuffer);
|
||||
@@ -200,25 +200,24 @@ class MPU6050Driver : public Usermod {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
int reading = 20;
|
||||
//this code adds "u":{"Light":[20," lux"]} to the info object
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray imu_meas = user.createNestedObject("IMU");
|
||||
StaticJsonDocument<600> doc; //measured 528
|
||||
|
||||
JsonObject imu_meas = doc.createNestedObject("IMU");
|
||||
#ifdef WLED_DEBUG
|
||||
JsonArray quat_json = imu_meas.createNestedArray("Quat");
|
||||
quat_json.add(qat.w);
|
||||
quat_json.add(qat.x);
|
||||
quat_json.add(qat.y);
|
||||
quat_json.add(qat.z);
|
||||
JsonArray euler_json = imu_meas.createNestedArray("Euler");
|
||||
euler_json.add(euler[0]);
|
||||
euler_json.add(euler[1]);
|
||||
euler_json.add(euler[2]);
|
||||
euler_json.add(euler[0] * 180/M_PI);
|
||||
euler_json.add(euler[1] * 180/M_PI);
|
||||
euler_json.add(euler[2] * 180/M_PI);
|
||||
JsonArray accel_json = imu_meas.createNestedArray("Accel");
|
||||
accel_json.add(aa.x);
|
||||
accel_json.add(aa.y);
|
||||
@@ -227,10 +226,6 @@ class MPU6050Driver : public Usermod {
|
||||
gyro_json.add(gy.x);
|
||||
gyro_json.add(gy.y);
|
||||
gyro_json.add(gy.z);
|
||||
JsonArray world_json = imu_meas.createNestedArray("WorldAccel");
|
||||
world_json.add(aaWorld.x);
|
||||
world_json.add(aaWorld.y);
|
||||
world_json.add(aaWorld.z);
|
||||
JsonArray real_json = imu_meas.createNestedArray("RealAccel");
|
||||
real_json.add(aaReal.x);
|
||||
real_json.add(aaReal.y);
|
||||
@@ -239,49 +234,54 @@ class MPU6050Driver : public Usermod {
|
||||
grav_json.add(gravity.x);
|
||||
grav_json.add(gravity.y);
|
||||
grav_json.add(gravity.z);
|
||||
JsonArray orient_json = imu_meas.createNestedArray("Orientation");
|
||||
orient_json.add(ypr[0]);
|
||||
orient_json.add(ypr[1]);
|
||||
orient_json.add(ypr[2]);
|
||||
#endif
|
||||
JsonArray world_json = imu_meas.createNestedArray("WorldAccel");
|
||||
world_json.add(aaWorld.x);
|
||||
world_json.add(aaWorld.y);
|
||||
world_json.add(aaWorld.z);
|
||||
JsonArray orient_json = imu_meas.createNestedArray("YPR");
|
||||
orient_json.add(ypr[0] * 180/M_PI);
|
||||
orient_json.add(ypr[1] * 180/M_PI);
|
||||
orient_json.add(ypr[2] * 180/M_PI);
|
||||
|
||||
char stringBuffer[300]; // measured 266
|
||||
serializeJson(imu_meas, stringBuffer);
|
||||
JsonArray mainObject = user.createNestedArray("IMU");
|
||||
mainObject.add(stringBuffer);
|
||||
|
||||
// Serial.printf("imu_meas %u (%u %u) stringBuffer %u\n", (unsigned int)imu_meas.memoryUsage(), (unsigned int)imu_meas.size(), (unsigned int)imu_meas.nesting(), strlen(stringBuffer));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
//void addToJsonState(JsonObject& root)
|
||||
//{
|
||||
//root["user0"] = userVar0;
|
||||
//}
|
||||
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
//void readFromJsonState(JsonObject& root)
|
||||
//{
|
||||
//if (root["bri"] == 255) DEBUG_PRINTLN(F("Don't burn down your garage!"));
|
||||
//}
|
||||
|
||||
// void addToConfig(JsonObject& root)
|
||||
// {
|
||||
// JsonObject top = root.createNestedObject("MPU6050");
|
||||
// top[FPSTR("enabled")] = enabled;
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
// void addToConfig(JsonObject& root)
|
||||
// {
|
||||
// JsonObject top = root.createNestedObject("MPU6050_IMU");
|
||||
// JsonArray pins = top.createNestedArray("pin");
|
||||
// pins.add(HW_PIN_SCL);
|
||||
// pins.add(HW_PIN_SDA);
|
||||
// }
|
||||
// JsonObject interruptPin = top.createNestedObject(FPSTR("interruptPin"));
|
||||
// interruptPin["pin"] = interruptPin;
|
||||
// }
|
||||
|
||||
// bool readFromConfig(JsonObject& root)
|
||||
// {
|
||||
// JsonObject top = root[FPSTR("MPU6050")];
|
||||
// bool configComplete = !top.isNull();
|
||||
|
||||
// configComplete &= getJsonValue(top[FPSTR("enabled")], enabled);
|
||||
// configComplete &= getJsonValue(top[FPSTR("interruptPin")]["pin"], interruptPin);
|
||||
|
||||
// return configComplete;
|
||||
// }
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_IMU;
|
||||
|
||||
Reference in New Issue
Block a user