mirror of
https://github.com/SlimeVR/SlimeVR-Tracker-ESP.git
synced 2026-04-06 02:01:57 +02:00
19
ci/build.py
19
ci/build.py
@@ -2,6 +2,7 @@ import json
|
||||
import os
|
||||
import shutil
|
||||
from enum import Enum
|
||||
from textwrap import dedent
|
||||
from typing import List
|
||||
|
||||
COLOR_ESC = '\033['
|
||||
@@ -24,11 +25,19 @@ class DeviceConfiguration:
|
||||
self.platformio_board = platformio_board
|
||||
|
||||
def get_platformio_section(self) -> str:
|
||||
return f"""
|
||||
[env:{self.platformio_board}]
|
||||
platform = {self.platform}
|
||||
board = {self.platformio_board}
|
||||
"""
|
||||
section = dedent(f"""
|
||||
[env:{self.platformio_board}]
|
||||
platform = {self.platform}
|
||||
board = {self.platformio_board}""")
|
||||
|
||||
if self.platform == "espressif32":
|
||||
section += dedent("""
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
lorol/LittleFS_esp32 @ 1.0.6
|
||||
""")
|
||||
|
||||
return section
|
||||
|
||||
def filename(self) -> str:
|
||||
return f"{self.platformio_board}.bin"
|
||||
|
||||
@@ -53,6 +53,9 @@ upload_speed = 921600
|
||||
; Uncomment below if you want to build for esp32
|
||||
; Check your board name at https://docs.platformio.org/en/latest/platforms/espressif32.html#boards
|
||||
; [env:esp32]
|
||||
; lib_deps =
|
||||
; ${env.lib_deps}
|
||||
; lorol/LittleFS_esp32 @ 1.0.6
|
||||
; platform = espressif32
|
||||
; board = esp32dev
|
||||
; Comment out this line below if you have any trouble uploading the firmware - and if it has a CP2102 on it (a square chip next to the usb port): change to 3000000 (3 million) for even faster upload speed
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
|
||||
#include "LEDManager.h"
|
||||
#include "status/StatusManager.h"
|
||||
#include "configuration/Configuration.h"
|
||||
|
||||
extern SlimeVR::LEDManager ledManager;
|
||||
extern SlimeVR::Status::StatusManager statusManager;
|
||||
extern SlimeVR::Configuration::Configuration configuration;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain
|
||||
Copyright (c) 2022 TheDevMinerTV
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,43 +21,25 @@
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <EEPROM.h>
|
||||
#include "configuration.h"
|
||||
#include "CalibrationConfig.h"
|
||||
|
||||
DeviceConfig config{};
|
||||
bool configLoaded;
|
||||
|
||||
void initializeConfig() {
|
||||
EEPROM.begin(sizeof(DeviceConfig) + 1);
|
||||
}
|
||||
|
||||
bool hasConfigStored() {
|
||||
bool hasConfigStored = false;
|
||||
EEPROM.get(0, hasConfigStored);
|
||||
return hasConfigStored;
|
||||
}
|
||||
|
||||
DeviceConfig *const getConfigPtr()
|
||||
{
|
||||
if (!configLoaded)
|
||||
{
|
||||
initializeConfig();
|
||||
if (hasConfigStored())
|
||||
{
|
||||
EEPROM.get(1, config);
|
||||
namespace SlimeVR {
|
||||
namespace Configuration {
|
||||
const char* calibrationConfigTypeToString(CalibrationConfigType type) {
|
||||
switch (type) {
|
||||
case NONE:
|
||||
return "NONE";
|
||||
case BMI160:
|
||||
return "BMI160";
|
||||
case MPU6050:
|
||||
return "MPU6050";
|
||||
case MPU9250:
|
||||
return "MPU9250";
|
||||
case ICM20948:
|
||||
return "ICM20948";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
configLoaded = true;
|
||||
}
|
||||
return &config;
|
||||
}
|
||||
|
||||
void setConfig(const DeviceConfig & newConfig) {
|
||||
config = newConfig;
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
void saveConfig() {
|
||||
EEPROM.put(0, true);
|
||||
EEPROM.put(1, config);
|
||||
EEPROM.commit();
|
||||
}
|
||||
92
src/configuration/CalibrationConfig.h
Normal file
92
src/configuration/CalibrationConfig.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2022 TheDevMinerTV
|
||||
|
||||
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 SLIMEVR_CONFIGURATION_CALIBRATIONCONFIG_H
|
||||
#define SLIMEVR_CONFIGURATION_CALIBRATIONCONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace SlimeVR {
|
||||
namespace Configuration {
|
||||
struct BMI160CalibrationConfig {
|
||||
// accelerometer offsets and correction matrix
|
||||
float A_B[3];
|
||||
float A_Ainv[3][3];
|
||||
|
||||
// raw offsets, determined from gyro at rest
|
||||
float G_off[3];
|
||||
|
||||
// calibration temperature for dynamic compensation
|
||||
float temperature;
|
||||
};
|
||||
|
||||
struct MPU6050CalibrationConfig {
|
||||
// accelerometer offsets and correction matrix
|
||||
float A_B[3];
|
||||
|
||||
// raw offsets, determined from gyro at rest
|
||||
float G_off[3];
|
||||
};
|
||||
|
||||
struct MPU9250CalibrationConfig {
|
||||
// accelerometer offsets and correction matrix
|
||||
float A_B[3];
|
||||
float A_Ainv[3][3];
|
||||
|
||||
// magnetometer offsets and correction matrix
|
||||
float M_B[3];
|
||||
float M_Ainv[3][3];
|
||||
|
||||
// raw offsets, determined from gyro at rest
|
||||
float G_off[3];
|
||||
};
|
||||
|
||||
struct ICM20948CalibrationConfig {
|
||||
// gyroscope bias
|
||||
int32_t G[3];
|
||||
|
||||
// accelerometer bias
|
||||
int32_t A[3];
|
||||
|
||||
// compass bias
|
||||
int32_t C[3];
|
||||
};
|
||||
|
||||
enum CalibrationConfigType { NONE, BMI160, MPU6050, MPU9250, ICM20948 };
|
||||
|
||||
const char* calibrationConfigTypeToString(CalibrationConfigType type);
|
||||
|
||||
struct CalibrationConfig {
|
||||
CalibrationConfigType type;
|
||||
|
||||
union {
|
||||
BMI160CalibrationConfig bmi160;
|
||||
MPU6050CalibrationConfig mpu6050;
|
||||
MPU9250CalibrationConfig mpu9250;
|
||||
ICM20948CalibrationConfig icm20948;
|
||||
} data;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
305
src/configuration/Configuration.cpp
Normal file
305
src/configuration/Configuration.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2022 TheDevMinerTV
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifdef ESP32
|
||||
// #define CONFIG_LITTLEFS_SPIFFS_COMPAT 1
|
||||
// #define CONFIG_LITTLEFS_LFS_YES_TRACE
|
||||
// #define LFS_YES_TRACE
|
||||
#include <LITTLEFS.h>
|
||||
#define LittleFS LITTLEFS
|
||||
#else
|
||||
#include <LittleFS.h>
|
||||
#endif
|
||||
|
||||
#include "Configuration.h"
|
||||
#include "consts.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace SlimeVR {
|
||||
namespace Configuration {
|
||||
CalibrationConfig Configuration::m_EmptyCalibration = {NONE};
|
||||
|
||||
void Configuration::setup() {
|
||||
if (m_Loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if ESP32
|
||||
LittleFS.begin(true);
|
||||
#else
|
||||
LittleFS.begin();
|
||||
#endif
|
||||
|
||||
if (LittleFS.exists("/config.bin")) {
|
||||
m_Logger.trace("Found configuration file");
|
||||
|
||||
File file = LittleFS.open("/config.bin", "r");
|
||||
|
||||
file.read((uint8_t*)&m_Config.version, sizeof(int32_t));
|
||||
|
||||
if (m_Config.version < CURRENT_CONFIGURATION_VERSION) {
|
||||
m_Logger.debug("Configuration is outdated: v%d < v%d", m_Config.version, CURRENT_CONFIGURATION_VERSION);
|
||||
|
||||
if (!runMigrations(m_Config.version)) {
|
||||
m_Logger.error("Failed to migrate configuration from v%d to v%d", m_Config.version, CURRENT_CONFIGURATION_VERSION);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
m_Logger.info("Found up-to-date configuration v%d", m_Config.version);
|
||||
}
|
||||
|
||||
file.seek(0);
|
||||
file.read((uint8_t*)&m_Config, sizeof(DeviceConfig));
|
||||
file.close();
|
||||
} else {
|
||||
m_Logger.info("No configuration file found, creating new one");
|
||||
m_Config.version = CURRENT_CONFIGURATION_VERSION;
|
||||
save();
|
||||
}
|
||||
|
||||
loadCalibrations();
|
||||
|
||||
m_Loaded = true;
|
||||
|
||||
m_Logger.info("Loaded configuration");
|
||||
|
||||
#ifdef FULL_DEBUG
|
||||
print();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Configuration::save() {
|
||||
for (size_t i = 0; i < m_Calibrations.size(); i++) {
|
||||
CalibrationConfig config = m_Calibrations[i];
|
||||
if (config.type == CalibrationConfigType::NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char path[17];
|
||||
sprintf(path, "/calibrations/%d", i);
|
||||
|
||||
m_Logger.trace("Saving calibration data for %d", i);
|
||||
|
||||
File file = LittleFS.open(path, "w");
|
||||
file.write((uint8_t*)&config, sizeof(CalibrationConfig));
|
||||
file.close();
|
||||
}
|
||||
|
||||
{
|
||||
File file = LittleFS.open("/config.bin", "w");
|
||||
file.write((uint8_t*)&m_Config, sizeof(DeviceConfig));
|
||||
file.close();
|
||||
}
|
||||
|
||||
m_Logger.debug("Saved configuration");
|
||||
}
|
||||
|
||||
void Configuration::reset() {
|
||||
LittleFS.format();
|
||||
|
||||
m_Calibrations.clear();
|
||||
m_Config.version = 1;
|
||||
save();
|
||||
|
||||
m_Logger.debug("Reset configuration");
|
||||
}
|
||||
|
||||
int32_t Configuration::getVersion() const {
|
||||
return m_Config.version;
|
||||
}
|
||||
|
||||
size_t Configuration::getCalibrationCount() const {
|
||||
return m_Calibrations.size();
|
||||
}
|
||||
|
||||
CalibrationConfig Configuration::getCalibration(size_t sensorID) const {
|
||||
if (sensorID >= m_Calibrations.size()) {
|
||||
return m_EmptyCalibration;
|
||||
}
|
||||
|
||||
return m_Calibrations.at(sensorID);
|
||||
}
|
||||
|
||||
void Configuration::setCalibration(size_t sensorID, const CalibrationConfig& config) {
|
||||
size_t currentCalibrations = m_Calibrations.size();
|
||||
|
||||
if (sensorID >= currentCalibrations) {
|
||||
m_Calibrations.resize(sensorID + 1, m_EmptyCalibration);
|
||||
}
|
||||
|
||||
m_Calibrations[sensorID] = config;
|
||||
}
|
||||
|
||||
void Configuration::loadCalibrations() {
|
||||
#ifdef ESP32
|
||||
{
|
||||
File calibrations = LittleFS.open("/calibrations");
|
||||
if (!calibrations) {
|
||||
m_Logger.warn("No calibration data found, creating new directory...");
|
||||
|
||||
if (!LittleFS.mkdir("/calibrations")) {
|
||||
m_Logger.error("Failed to create directory: /calibrations");
|
||||
return;
|
||||
}
|
||||
|
||||
calibrations = LittleFS.open("/calibrations");
|
||||
}
|
||||
|
||||
if (!calibrations.isDirectory()) {
|
||||
calibrations.close();
|
||||
|
||||
m_Logger.warn("Found file instead of directory: /calibrations");
|
||||
|
||||
if (!LittleFS.remove("/calibrations")) {
|
||||
m_Logger.error("Failed to remove directory: /calibrations");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LittleFS.mkdir("/calibrations")) {
|
||||
m_Logger.error("Failed to create directory: /calibrations");
|
||||
return;
|
||||
}
|
||||
|
||||
calibrations = LittleFS.open("/calibrations");
|
||||
}
|
||||
|
||||
m_Logger.debug("Found calibration data directory");
|
||||
|
||||
while (File f = calibrations.openNextFile()) {
|
||||
if (f.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_Logger.trace("Found calibration data file: %s", f.name());
|
||||
|
||||
CalibrationConfig calibrationConfig;
|
||||
f.read((uint8_t*)&calibrationConfig, sizeof(CalibrationConfig));
|
||||
f.close();
|
||||
|
||||
uint8_t sensorId = strtoul(calibrations.name(), nullptr, 10);
|
||||
m_Logger.debug("Found sensor calibration for %s at index %d", calibrationConfigTypeToString(calibrationConfig.type), sensorId);
|
||||
|
||||
setCalibration(sensorId, calibrationConfig);
|
||||
}
|
||||
|
||||
calibrations.close();
|
||||
}
|
||||
#else
|
||||
{
|
||||
if (!LittleFS.exists("/calibrations")) {
|
||||
m_Logger.warn("No calibration data found, creating new directory...");
|
||||
|
||||
if (!LittleFS.mkdir("/calibrations")) {
|
||||
m_Logger.error("Failed to create directory: /calibrations");
|
||||
return;
|
||||
}
|
||||
|
||||
// There's no calibrations here, so we're done
|
||||
return;
|
||||
}
|
||||
|
||||
Dir calibrations = LittleFS.openDir("/calibrations");
|
||||
while (calibrations.next()) {
|
||||
File f = calibrations.openFile("r");
|
||||
if (!f.isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CalibrationConfig calibrationConfig;
|
||||
f.read((uint8_t*)&calibrationConfig, sizeof(CalibrationConfig));
|
||||
|
||||
uint8_t sensorId = strtoul(calibrations.fileName().c_str(), nullptr, 10);
|
||||
m_Logger.debug("Found sensor calibration for %s at index %d", calibrationConfigTypeToString(calibrationConfig.type), sensorId);
|
||||
|
||||
setCalibration(sensorId, calibrationConfig);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Configuration::runMigrations(int32_t version) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Configuration::print() {
|
||||
m_Logger.info("Configuration:");
|
||||
m_Logger.info(" Version: %d", m_Config.version);
|
||||
m_Logger.info(" %d Calibrations:", m_Calibrations.size());
|
||||
|
||||
for (size_t i = 0; i < m_Calibrations.size(); i++) {
|
||||
const CalibrationConfig& c = m_Calibrations[i];
|
||||
m_Logger.info(" - [%3d] %s", i, calibrationConfigTypeToString(c.type));
|
||||
|
||||
switch (c.type) {
|
||||
case CalibrationConfigType::NONE:
|
||||
break;
|
||||
|
||||
case CalibrationConfigType::BMI160:
|
||||
m_Logger.info(" A_B : %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.bmi160.A_B));
|
||||
|
||||
m_Logger.info(" A_Ainv :");
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
m_Logger.info(" %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.bmi160.A_Ainv[i]));
|
||||
}
|
||||
|
||||
m_Logger.info(" G_off : %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.bmi160.G_off));
|
||||
m_Logger.info(" Temperature: %f", c.data.bmi160.temperature);
|
||||
|
||||
break;
|
||||
|
||||
case CalibrationConfigType::ICM20948:
|
||||
m_Logger.info(" G: %d, %d, %d", UNPACK_VECTOR_ARRAY(c.data.icm20948.G));
|
||||
m_Logger.info(" A: %d, %d, %d", UNPACK_VECTOR_ARRAY(c.data.icm20948.A));
|
||||
m_Logger.info(" C: %d, %d, %d", UNPACK_VECTOR_ARRAY(c.data.icm20948.C));
|
||||
|
||||
break;
|
||||
|
||||
case CalibrationConfigType::MPU9250:
|
||||
m_Logger.info(" A_B : %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.mpu9250.A_B));
|
||||
|
||||
m_Logger.info(" A_Ainv:");
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
m_Logger.info(" %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.mpu9250.A_Ainv[i]));
|
||||
}
|
||||
|
||||
m_Logger.info(" M_B : %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.mpu9250.M_B));
|
||||
|
||||
m_Logger.info(" M_Ainv:");
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
m_Logger.info(" %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.mpu9250.M_Ainv[i]));
|
||||
}
|
||||
|
||||
m_Logger.info(" G_off : %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.mpu9250.G_off));
|
||||
|
||||
break;
|
||||
|
||||
case CalibrationConfigType::MPU6050:
|
||||
m_Logger.info(" A_B : %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.mpu6050.A_B));
|
||||
m_Logger.info(" G_off: %f, %f, %f", UNPACK_VECTOR_ARRAY(c.data.mpu6050.G_off));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/configuration/Configuration.h
Normal file
65
src/configuration/Configuration.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2022 TheDevMinerTV
|
||||
|
||||
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 SLIMEVR_CONFIGURATION_CONFIGURATION_H
|
||||
#define SLIMEVR_CONFIGURATION_CONFIGURATION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DeviceConfig.h"
|
||||
#include "logging/Logger.h"
|
||||
|
||||
namespace SlimeVR {
|
||||
namespace Configuration {
|
||||
class Configuration {
|
||||
public:
|
||||
void setup();
|
||||
|
||||
void save();
|
||||
void reset();
|
||||
|
||||
void print();
|
||||
|
||||
int32_t getVersion() const;
|
||||
|
||||
size_t getCalibrationCount() const;
|
||||
CalibrationConfig getCalibration(size_t sensorID) const;
|
||||
void setCalibration(size_t sensorID, const CalibrationConfig& config);
|
||||
|
||||
private:
|
||||
void loadCalibrations();
|
||||
bool runMigrations(int32_t version);
|
||||
|
||||
bool m_Loaded = false;
|
||||
|
||||
DeviceConfig m_Config{};
|
||||
std::vector<CalibrationConfig> m_Calibrations;
|
||||
|
||||
Logging::Logger m_Logger = Logging::Logger("Configuration");
|
||||
|
||||
static CalibrationConfig m_EmptyCalibration;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain
|
||||
Copyright (c) 2022 TheDevMinerTV
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,30 +21,17 @@
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SLIMEVR_CONFIG_H_
|
||||
#define SLIMEVR_CONFIG_H_
|
||||
#ifndef SLIMEVR_CONFIGURATION_DEVICECONFIG_H
|
||||
#define SLIMEVR_CONFIGURATION_DEVICECONFIG_H
|
||||
|
||||
struct CalibrationConfig {
|
||||
//accel offsets and correction matrix
|
||||
float A_B[3];
|
||||
float A_Ainv[3][3];
|
||||
// mag offsets and correction matrix
|
||||
float M_B[3];
|
||||
float M_Ainv[3][3];
|
||||
//raw offsets, determined for gyro at rest
|
||||
float G_off[3];
|
||||
// calibration temperature for dynamic compensation
|
||||
float temperature;
|
||||
};
|
||||
#include "CalibrationConfig.h"
|
||||
|
||||
struct DeviceConfig {
|
||||
CalibrationConfig calibration[2];
|
||||
int deviceId;
|
||||
int deviceMode;
|
||||
};
|
||||
namespace SlimeVR {
|
||||
namespace Configuration {
|
||||
struct DeviceConfig {
|
||||
int32_t version;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
DeviceConfig * const getConfigPtr();
|
||||
void setConfig(const DeviceConfig & config);
|
||||
void saveConfig();
|
||||
|
||||
#endif // SLIMEVR_CONFIG_H_
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain
|
||||
Copyright (c) 2021 Eiren Rain & SlimeVR contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -72,4 +72,6 @@
|
||||
#define HARDWARE_MCU 0
|
||||
#endif
|
||||
|
||||
#define CURRENT_CONFIGURATION_VERSION 1
|
||||
|
||||
#endif // SLIMEVR_CONSTS_H_
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "Wire.h"
|
||||
#include "ota.h"
|
||||
#include "sensors/SensorManager.h"
|
||||
#include "configuration.h"
|
||||
#include "configuration/Configuration.h"
|
||||
#include "network/network.h"
|
||||
#include "globals.h"
|
||||
#include "credentials.h"
|
||||
@@ -39,6 +39,7 @@ SlimeVR::Logging::Logger logger("SlimeVR");
|
||||
SlimeVR::Sensors::SensorManager sensorManager;
|
||||
SlimeVR::LEDManager ledManager(LED_PIN);
|
||||
SlimeVR::Status::StatusManager statusManager;
|
||||
SlimeVR::Configuration::Configuration configuration;
|
||||
|
||||
int sensorToCalibrate = -1;
|
||||
bool blinking = false;
|
||||
@@ -61,6 +62,7 @@ void setup()
|
||||
statusManager.setStatus(SlimeVR::Status::LOADING, true);
|
||||
|
||||
ledManager.setup();
|
||||
configuration.setup();
|
||||
|
||||
SerialCommands::setUp();
|
||||
|
||||
@@ -75,7 +77,6 @@ void setup()
|
||||
#endif
|
||||
Wire.setClock(I2C_SPEED);
|
||||
|
||||
getConfigPtr();
|
||||
// Wait for IMU to boot
|
||||
delay(500);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain
|
||||
Copyright (c) 2021 Eiren Rain & SlimeVR contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <WiFiUdp.h>
|
||||
#include <Arduino.h>
|
||||
#include "quat.h"
|
||||
#include "configuration.h"
|
||||
#include "sensors/sensor.h"
|
||||
#include "wifihandler.h"
|
||||
#include "globals.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain
|
||||
Copyright (c) 2021 Eiren Rain & SlimeVR contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,6 @@
|
||||
#ifndef SLIMEVR_WIFI_H_
|
||||
#define SLIMEVR_WIFI_H_
|
||||
|
||||
#include "configuration.h"
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 S.J. Remington, SlimeVR contributors
|
||||
Copyright (c) 2021 S.J. Remington & SlimeVR contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -85,8 +85,26 @@ void BMI160Sensor::motionSetup() {
|
||||
ledManager.off();
|
||||
}
|
||||
|
||||
DeviceConfig * const config = getConfigPtr();
|
||||
calibration = &config->calibration[sensorId];
|
||||
// Initialize the configuration
|
||||
{
|
||||
SlimeVR::Configuration::CalibrationConfig sensorCalibration = configuration.getCalibration(sensorId);
|
||||
// If no compatible calibration data is found, the calibration data will just be zero-ed out
|
||||
switch (sensorCalibration.type) {
|
||||
case SlimeVR::Configuration::CalibrationConfigType::BMI160:
|
||||
m_Calibration = sensorCalibration.data.bmi160;
|
||||
break;
|
||||
|
||||
case SlimeVR::Configuration::CalibrationConfigType::NONE:
|
||||
m_Logger.warn("No calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
break;
|
||||
|
||||
default:
|
||||
m_Logger.warn("Incompatible calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
}
|
||||
}
|
||||
|
||||
working = true;
|
||||
}
|
||||
|
||||
@@ -148,7 +166,7 @@ void BMI160Sensor::getScaledValues(float Gxyz[3], float Axyz[3])
|
||||
#endif
|
||||
|
||||
float temperature = getTemperature();
|
||||
float tempDiff = temperature - calibration->temperature;
|
||||
float tempDiff = temperature - m_Calibration.temperature;
|
||||
uint8_t quant = map(temperature, 15, 75, 0, 12);
|
||||
|
||||
int16_t ax, ay, az;
|
||||
@@ -158,9 +176,9 @@ void BMI160Sensor::getScaledValues(float Gxyz[3], float Axyz[3])
|
||||
|
||||
// TODO: Sensitivity over temp compensation?
|
||||
// TODO: Cross-axis sensitivity compensation?
|
||||
Gxyz[0] = ((float)gx - (calibration->G_off[0] + (tempDiff * LSB_COMP_PER_TEMP_X_MAP[quant]))) * GSCALE;
|
||||
Gxyz[1] = ((float)gy - (calibration->G_off[1] + (tempDiff * LSB_COMP_PER_TEMP_Y_MAP[quant]))) * GSCALE;
|
||||
Gxyz[2] = ((float)gz - (calibration->G_off[2] + (tempDiff * LSB_COMP_PER_TEMP_Z_MAP[quant]))) * GSCALE;
|
||||
Gxyz[0] = ((float)gx - (m_Calibration.G_off[0] + (tempDiff * LSB_COMP_PER_TEMP_X_MAP[quant]))) * GSCALE;
|
||||
Gxyz[1] = ((float)gy - (m_Calibration.G_off[1] + (tempDiff * LSB_COMP_PER_TEMP_Y_MAP[quant]))) * GSCALE;
|
||||
Gxyz[2] = ((float)gz - (m_Calibration.G_off[2] + (tempDiff * LSB_COMP_PER_TEMP_Z_MAP[quant]))) * GSCALE;
|
||||
|
||||
Axyz[0] = (float)ax;
|
||||
Axyz[1] = (float)ay;
|
||||
@@ -169,10 +187,10 @@ void BMI160Sensor::getScaledValues(float Gxyz[3], float Axyz[3])
|
||||
#if useFullCalibrationMatrix == true
|
||||
float temp[3];
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
temp[i] = (Axyz[i] - calibration->A_B[i]);
|
||||
Axyz[0] = calibration->A_Ainv[0][0] * temp[0] + calibration->A_Ainv[0][1] * temp[1] + calibration->A_Ainv[0][2] * temp[2];
|
||||
Axyz[1] = calibration->A_Ainv[1][0] * temp[0] + calibration->A_Ainv[1][1] * temp[1] + calibration->A_Ainv[1][2] * temp[2];
|
||||
Axyz[2] = calibration->A_Ainv[2][0] * temp[0] + calibration->A_Ainv[2][1] * temp[1] + calibration->A_Ainv[2][2] * temp[2];
|
||||
temp[i] = (Axyz[i] - m_Calibration.A_B[i]);
|
||||
Axyz[0] = m_Calibration.A_Ainv[0][0] * temp[0] + m_Calibration.A_Ainv[0][1] * temp[1] + m_Calibration.A_Ainv[0][2] * temp[2];
|
||||
Axyz[1] = m_Calibration.A_Ainv[1][0] * temp[0] + m_Calibration.A_Ainv[1][1] * temp[1] + m_Calibration.A_Ainv[1][2] * temp[2];
|
||||
Axyz[2] = m_Calibration.A_Ainv[2][0] * temp[0] + m_Calibration.A_Ainv[2][1] * temp[1] + m_Calibration.A_Ainv[2][2] * temp[2];
|
||||
#else
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
Axyz[i] = (Axyz[i] - calibration->A_B[i]);
|
||||
@@ -183,13 +201,12 @@ void BMI160Sensor::startCalibration(int calibrationType) {
|
||||
ledManager.on();
|
||||
|
||||
m_Logger.debug("Gathering raw data for device calibration...");
|
||||
DeviceConfig * const config = getConfigPtr();
|
||||
|
||||
// Wait for sensor to calm down before calibration
|
||||
m_Logger.info("Put down the device and wait for baseline gyro reading calibration");
|
||||
delay(2000);
|
||||
float temperature = getTemperature();
|
||||
config->calibration[sensorId].temperature = temperature;
|
||||
m_Calibration.temperature = temperature;
|
||||
uint16_t gyroCalibrationSamples = 2500;
|
||||
float rawGxyz[3] = {0};
|
||||
|
||||
@@ -209,12 +226,12 @@ void BMI160Sensor::startCalibration(int calibrationType) {
|
||||
|
||||
ledManager.off();
|
||||
}
|
||||
config->calibration[sensorId].G_off[0] = rawGxyz[0] / gyroCalibrationSamples;
|
||||
config->calibration[sensorId].G_off[1] = rawGxyz[1] / gyroCalibrationSamples;
|
||||
config->calibration[sensorId].G_off[2] = rawGxyz[2] / gyroCalibrationSamples;
|
||||
m_Calibration.G_off[0] = rawGxyz[0] / gyroCalibrationSamples;
|
||||
m_Calibration.G_off[1] = rawGxyz[1] / gyroCalibrationSamples;
|
||||
m_Calibration.G_off[2] = rawGxyz[2] / gyroCalibrationSamples;
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("Gyro calibration results: %f %f %f", config->calibration[sensorId].G_off[0], config->calibration[sensorId].G_off[1], config->calibration[sensorId].G_off[2]);
|
||||
m_Logger.trace("Gyro calibration results: %f %f %f", UNPACK_VECTOR_ARRAY(m_Calibration[sensorId].G_off));
|
||||
#endif
|
||||
|
||||
// Blink calibrating led before user should rotate the sensor
|
||||
@@ -251,16 +268,24 @@ void BMI160Sensor::startCalibration(int calibrationType) {
|
||||
m_Logger.debug("{");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
config->calibration[sensorId].A_B[i] = A_BAinv[0][i];
|
||||
config->calibration[sensorId].A_Ainv[0][i] = A_BAinv[1][i];
|
||||
config->calibration[sensorId].A_Ainv[1][i] = A_BAinv[2][i];
|
||||
config->calibration[sensorId].A_Ainv[2][i] = A_BAinv[3][i];
|
||||
m_Calibration.A_B[i] = A_BAinv[0][i];
|
||||
m_Calibration.A_Ainv[0][i] = A_BAinv[1][i];
|
||||
m_Calibration.A_Ainv[1][i] = A_BAinv[2][i];
|
||||
m_Calibration.A_Ainv[2][i] = A_BAinv[3][i];
|
||||
m_Logger.debug(" %f, %f, %f, %f", A_BAinv[0][i], A_BAinv[1][i], A_BAinv[2][i], A_BAinv[3][i]);
|
||||
}
|
||||
m_Logger.debug("}");
|
||||
m_Logger.debug("Now Saving EEPROM");
|
||||
setConfig(*config);
|
||||
m_Logger.debug("Finished Saving EEPROM");
|
||||
|
||||
m_Logger.debug("Saving the calibration data");
|
||||
|
||||
SlimeVR::Configuration::CalibrationConfig calibration;
|
||||
calibration.type = SlimeVR::Configuration::CalibrationConfigType::BMI160;
|
||||
calibration.data.bmi160 = m_Calibration;
|
||||
configuration.setCalibration(sensorId, calibration);
|
||||
configuration.save();
|
||||
|
||||
m_Logger.debug("Saved the calibration data");
|
||||
|
||||
m_Logger.info("Calibration data gathered");
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
@@ -41,11 +41,12 @@ class BMI160Sensor : public Sensor {
|
||||
float getTemperature();
|
||||
private:
|
||||
BMI160 imu {};
|
||||
CalibrationConfig * calibration;
|
||||
float q[4] {1.0f, 0.0f, 0.0f, 0.0f};
|
||||
// Loop timing globals
|
||||
uint32_t now = 0, last = 0; //micros() timers
|
||||
float deltat = 0; //loop time in seconds
|
||||
|
||||
SlimeVR::Configuration::BMI160CalibrationConfig m_Calibration;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 S.J. Remington, SlimeVR contributors
|
||||
Copyright (c) 2021 S.J. Remington & SlimeVR contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "icm20948sensor.h"
|
||||
#include "calibration.h"
|
||||
#include <i2cscan.h>
|
||||
#include <EEPROM.h> // for 8266, save the current bias values to eeprom
|
||||
#include "network/network.h"
|
||||
#include "GlobalVars.h"
|
||||
|
||||
@@ -34,324 +33,122 @@ int bias_save_periods[] = { 120, 180, 300, 600, 600 }; // 2min + 3min + 5min + 1
|
||||
// #define ENABLE_TAP false
|
||||
// #endif
|
||||
|
||||
void ICM20948Sensor::save_bias(bool repeat) {
|
||||
#if defined(SAVE_BIAS) && SAVE_BIAS
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("Saving Bias");
|
||||
#endif
|
||||
#if ESP8266
|
||||
int8_t count;
|
||||
int32_t bias_a[3], bias_g[3], bias_m[3];
|
||||
|
||||
imu.GetBiasGyroX(&bias_g[0]);
|
||||
imu.GetBiasGyroY(&bias_g[1]);
|
||||
imu.GetBiasGyroZ(&bias_g[2]);
|
||||
|
||||
imu.GetBiasAccelX(&bias_a[0]);
|
||||
imu.GetBiasAccelY(&bias_a[1]);
|
||||
imu.GetBiasAccelZ(&bias_a[2]);
|
||||
|
||||
#if !USE_6_AXIS
|
||||
imu.GetBiasCPassX(&bias_m[0]);
|
||||
imu.GetBiasCPassY(&bias_m[1]);
|
||||
imu.GetBiasCPassZ(&bias_m[2]);
|
||||
#endif
|
||||
|
||||
bool gyro_set = bias_g[0] && bias_g[1] && bias_g[2];
|
||||
bool accel_set = bias_a[0] && bias_a[1] && bias_a[2];
|
||||
#if !USE_6_AXIS
|
||||
bool CPass_set = bias_m[0] && bias_m[1] && bias_m[2];
|
||||
#endif
|
||||
|
||||
EEPROM.begin(4096); // max memory usage = 4096
|
||||
EEPROM.get(addr + 100, count); // 1st imu counter in EEPROM addr: 0x69+100=205, 2nd addr: 0x68+100=204
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("[0x%02X] EEPROM position: %d, count: %d", addr, addr + 100, count);
|
||||
#endif
|
||||
|
||||
if(count < 0 || count > 42) {
|
||||
count = sensorId; // 1st imu counter is even number, 2nd is odd
|
||||
} else if(repeat) {
|
||||
count++;
|
||||
}
|
||||
EEPROM.put(addr + 100, count);
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("[0x%02X] bias gyro save(%d): [%d, %d, %d]", addr, count * 12, bias_g[0], bias_g[1], bias_g[2]);
|
||||
m_Logger.trace("[0x%02X] bias accel save(%d): [%d, %d, %d]", addr, count * 12, bias_a[0], bias_a[1], bias_a[2]);
|
||||
m_Logger.trace("[0x%02X] bias CPass save(%d): [%d, %d, %d]", addr, count * 12, bias_m[0], bias_m[1], bias_m[2]);
|
||||
#endif
|
||||
|
||||
if (gyro_set) {
|
||||
EEPROM.put(1024 + (count * 12), bias_g); // 1024 ~ 2008
|
||||
}
|
||||
if (accel_set) {
|
||||
EEPROM.put(2046 + (count * 12), bias_a); // 2046 ~ 3030
|
||||
}
|
||||
#if !USE_6_AXIS
|
||||
if (CPass_set) {
|
||||
EEPROM.put(3072 + (count * 12), bias_m); // 3072 ~ 4056
|
||||
}
|
||||
#endif
|
||||
EEPROM.end(); // save and end
|
||||
if (repeat) {
|
||||
bias_save_counter++;
|
||||
// Possible: Could make it repeat the final timer value if any of the biases are still 0. Save strategy could be improved.
|
||||
if (sizeof(bias_save_periods) != bias_save_counter) {
|
||||
timer.in(bias_save_periods[bias_save_counter] * 1000, [](void *arg) -> bool { ((ICM20948Sensor*)arg)->save_bias(true); return false; }, this);
|
||||
}
|
||||
}
|
||||
#elif ESP32
|
||||
int bias_a[3], bias_g[3], bias_m[3];
|
||||
|
||||
imu.GetBiasGyroX(&bias_g[0]);
|
||||
imu.GetBiasGyroY(&bias_g[1]);
|
||||
imu.GetBiasGyroZ(&bias_g[2]);
|
||||
|
||||
imu.GetBiasAccelX(&bias_a[0]);
|
||||
imu.GetBiasAccelY(&bias_a[1]);
|
||||
imu.GetBiasAccelZ(&bias_a[2]);
|
||||
|
||||
imu.GetBiasCPassX(&bias_m[0]);
|
||||
imu.GetBiasCPassY(&bias_m[1]);
|
||||
imu.GetBiasCPassZ(&bias_m[2]);
|
||||
|
||||
bool accel_set = bias_a[0] && bias_a[1] && bias_a[2];
|
||||
bool gyro_set = bias_g[0] && bias_g[1] && bias_g[2];
|
||||
bool mag_set = bias_m[0] && bias_m[1] && bias_m[2];
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("bias gyro on IMU: %d, %d, %d", bias_g[0], bias_g[1], bias_g[2]);
|
||||
m_Logger.trace("bias accel on IMU: %d, %d, %d", bias_a[0], bias_a[1], bias_a[2]);
|
||||
m_Logger.trace("bias mag on IMU: %d, %d, %d", bias_m[0], bias_m[1], bias_m[2]);
|
||||
#endif
|
||||
|
||||
bool auxiliary = sensorId == 1;
|
||||
if (accel_set) {
|
||||
// Save accel
|
||||
prefs.putInt(auxiliary ? "ba01" : "ba00", bias_a[0]);
|
||||
prefs.putInt(auxiliary ? "ba11" : "ba10", bias_a[1]);
|
||||
prefs.putInt(auxiliary ? "ba21" : "ba20", bias_a[2]);
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("Wrote Accel Bias");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gyro_set) {
|
||||
// Save gyro
|
||||
prefs.putInt(auxiliary ? "bg01" : "bg00", bias_g[0]);
|
||||
prefs.putInt(auxiliary ? "bg11" : "bg10", bias_g[1]);
|
||||
prefs.putInt(auxiliary ? "bg21" : "bg20", bias_g[2]);
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("Wrote Gyro Bias");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mag_set) {
|
||||
// Save mag
|
||||
prefs.putInt(auxiliary ? "bm01" : "bm00", bias_m[0]);
|
||||
prefs.putInt(auxiliary ? "bm11" : "bm10", bias_m[1]);
|
||||
prefs.putInt(auxiliary ? "bm21" : "bm20", bias_m[2]);
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("Wrote Mag Bias");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (repeat) {
|
||||
bias_save_counter++;
|
||||
// Possible: Could make it repeat the final timer value if any of the biases are still 0. Save strategy could be improved.
|
||||
if (sizeof(bias_save_periods) != bias_save_counter)
|
||||
{
|
||||
timer.in(bias_save_periods[bias_save_counter] * 1000, [](void *arg) -> bool { ((ICM20948Sensor*)arg)->save_bias(true); return false; }, this);
|
||||
}
|
||||
}
|
||||
|
||||
void ICM20948Sensor::save_bias(bool repeat) {
|
||||
#if defined(SAVE_BIAS) && SAVE_BIAS
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("Saving Bias");
|
||||
#endif
|
||||
|
||||
imu.GetBiasGyroX(&m_Calibration.G[0]);
|
||||
imu.GetBiasGyroY(&m_Calibration.G[1]);
|
||||
imu.GetBiasGyroZ(&m_Calibration.G[2]);
|
||||
|
||||
imu.GetBiasAccelX(&m_Calibration.A[0]);
|
||||
imu.GetBiasAccelY(&m_Calibration.A[1]);
|
||||
imu.GetBiasAccelZ(&m_Calibration.A[2]);
|
||||
|
||||
#if !USE_6_AXIS
|
||||
imu.GetBiasCPassX(&m_Calibration.C[0]);
|
||||
imu.GetBiasCPassY(&m_Calibration.C[1]);
|
||||
imu.GetBiasCPassZ(&m_Calibration.C[2]);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("Gyrometer bias: [%d, %d, %d]", UNPACK_VECTOR_ARRAY(m_Calibration.G));
|
||||
m_Logger.trace("Accelerometer bias: [%d, %d, %d]", UNPACK_VECTOR_ARRAY(m_Calibration.A));
|
||||
#if !USE_6_AXIS
|
||||
m_Logger.trace("Compass bias: [%d, %d, %d]", UNPACK_VECTOR_ARRAY(m_Calibration.C));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SlimeVR::Configuration::CalibrationConfig calibration;
|
||||
calibration.type = SlimeVR::Configuration::CalibrationConfigType::ICM20948;
|
||||
calibration.data.icm20948 = m_Calibration;
|
||||
configuration.setCalibration(sensorId, calibration);
|
||||
configuration.save();
|
||||
|
||||
if (repeat) {
|
||||
bias_save_counter++;
|
||||
// Possible: Could make it repeat the final timer value if any of the biases are still 0. Save strategy could be improved.
|
||||
if (sizeof(bias_save_periods) != bias_save_counter) {
|
||||
timer.in(
|
||||
bias_save_periods[bias_save_counter] * 1000,
|
||||
[](void* arg) -> bool {
|
||||
((ICM20948Sensor*)arg)->save_bias(true);
|
||||
return false;
|
||||
},
|
||||
this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ICM20948Sensor::load_bias() {
|
||||
#if defined(LOAD_BIAS) && LOAD_BIAS
|
||||
#if ESP8266
|
||||
int8_t count;
|
||||
int32_t bias_g[3], bias_a[3], bias_m[3];
|
||||
count = 0;
|
||||
EEPROM.begin(4096); // max memory usage = 4096
|
||||
EEPROM.get(addr + 100, count); // 1st imu counter in EEPROM addr: 0x69+100=205, 2nd addr: 0x68+100=204
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("[0x%02X] EEPROM position: %d, count: %d", addr, addr + 100, count);
|
||||
m_Logger.trace("Gyrometer bias: [%d, %d, %d]", UNPACK_VECTOR_ARRAY(m_Calibration.G));
|
||||
m_Logger.trace("Accelerometer bias: [%d, %d, %d]", UNPACK_VECTOR_ARRAY(m_Calibration.A));
|
||||
#if !USE_6_AXIS
|
||||
m_Logger.trace("Compass bias: [%d, %d, %d]", UNPACK_VECTOR_ARRAY(m_Calibration.C));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if(count < 0 || count > 42) {
|
||||
count = sensorId; // 1st imu counter is even number, 2nd is odd
|
||||
EEPROM.put(addr + 100, count);
|
||||
}
|
||||
#if defined(LOAD_BIAS) && LOAD_BIAS
|
||||
imu.SetBiasGyroX(m_Calibration.G[0]);
|
||||
imu.SetBiasGyroY(m_Calibration.G[1]);
|
||||
imu.SetBiasGyroZ(m_Calibration.G[2]);
|
||||
|
||||
EEPROM.get(1024 + (count * 12), bias_g); // 1024 ~ 2008
|
||||
EEPROM.get(2046 + (count * 12), bias_a); // 2046 ~ 3030
|
||||
EEPROM.get(3072 + (count * 12), bias_m); // 3072 ~ 4056
|
||||
EEPROM.end();
|
||||
imu.SetBiasAccelX(m_Calibration.A[0]);
|
||||
imu.SetBiasAccelY(m_Calibration.A[1]);
|
||||
imu.SetBiasAccelZ(m_Calibration.A[2]);
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("[0x%02X] EEPROM gyro get(%d): [%d, %d, %d]", addr, count * 12, bias_g[0], bias_g[1], bias_g[2]);
|
||||
m_Logger.trace("[0x%02X] EEPROM accel get(%d): [%d, %d, %d]", addr, count * 12, bias_a[0], bias_a[1], bias_a[2]);
|
||||
m_Logger.trace("[0x%02X] EEPROM CPass get(%d): [%d, %d, %d]", addr, count * 12, bias_m[0], bias_m[1], bias_m[2]);
|
||||
#if !USE_6_AXIS
|
||||
imu.SetBiasCPassX(m_Calibration.C[0]);
|
||||
imu.SetBiasCPassY(m_Calibration.C[1]);
|
||||
imu.SetBiasCPassZ(m_Calibration.C[2]);
|
||||
#endif
|
||||
#else
|
||||
#if BIAS_DEBUG
|
||||
// Sets all bias to 90
|
||||
imu.SetBiasGyroX(90);
|
||||
imu.SetBiasGyroY(90);
|
||||
imu.SetBiasGyroZ(90);
|
||||
|
||||
imu.SetBiasAccelX(90);
|
||||
imu.SetBiasAccelY(90);
|
||||
imu.SetBiasAccelZ(90);
|
||||
|
||||
imu.SetBiasCPassX(90);
|
||||
imu.SetBiasCPassY(90);
|
||||
imu.SetBiasCPassZ(90);
|
||||
|
||||
int bias_gyro[3], bias_accel[3], bias_compass[3];
|
||||
|
||||
// Reloads all bias from memory
|
||||
imu.GetBiasGyroX(&bias_gyro[0]);
|
||||
imu.GetBiasGyroY(&bias_gyro[1]);
|
||||
imu.GetBiasGyroZ(&bias_gyro[2]);
|
||||
|
||||
imu.GetBiasAccelX(&bias_accel[0]);
|
||||
imu.GetBiasAccelY(&bias_accel[1]);
|
||||
imu.GetBiasAccelZ(&bias_accel[2]);
|
||||
|
||||
imu.GetBiasCPassX(&bias_compass[0]);
|
||||
imu.GetBiasCPassY(&bias_compass[1]);
|
||||
imu.GetBiasCPassZ(&bias_compass[2]);
|
||||
|
||||
#ifdef FULL_DEBUG
|
||||
m_Logger.trace("All set bias should be 90");
|
||||
|
||||
m_Logger.trace("Gyrometer bias : [%d, %d, %d]", UNPACK_VECTOR_ARRAY(bias_gyro));
|
||||
m_Logger.trace("Accelerometer bias: [%d, %d, %d]", UNPACK_VECTOR_ARRAY(bias_accel));
|
||||
m_Logger.trace("Compass bias : [%d, %d, %d]", UNPACK_VECTOR_ARRAY(bias_compass));
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
imu.SetBiasGyroX(bias_g[0]);
|
||||
imu.SetBiasGyroY(bias_g[1]);
|
||||
imu.SetBiasGyroZ(bias_g[2]);
|
||||
|
||||
imu.SetBiasAccelX(bias_a[0]);
|
||||
imu.SetBiasAccelY(bias_a[1]);
|
||||
imu.SetBiasAccelZ(bias_a[2]);
|
||||
|
||||
imu.SetBiasCPassX(bias_m[0]);
|
||||
imu.SetBiasCPassY(bias_m[1]);
|
||||
imu.SetBiasCPassZ(bias_m[2]);
|
||||
|
||||
#elif ESP32
|
||||
bool auxiliary = sensorId == 1;
|
||||
|
||||
int32_t bias_a[3], bias_g[3], bias_m[3];
|
||||
|
||||
bias_a[0] = prefs.getInt(auxiliary ? "ba01" : "ba00", 0);
|
||||
bias_a[1] = prefs.getInt(auxiliary ? "ba11" : "ba10", 0);
|
||||
bias_a[2] = prefs.getInt(auxiliary ? "ba21" : "ba20", 0);
|
||||
|
||||
bias_g[0] = prefs.getInt(auxiliary ? "bg01" : "bg00", 0);
|
||||
bias_g[1] = prefs.getInt(auxiliary ? "bg11" : "bg10", 0);
|
||||
bias_g[2] = prefs.getInt(auxiliary ? "bg21" : "bg20", 0);
|
||||
|
||||
bias_m[0] = prefs.getInt(auxiliary ? "bm01" : "bm00", 0);
|
||||
bias_m[1] = prefs.getInt(auxiliary ? "bm11" : "bm10", 0);
|
||||
bias_m[2] = prefs.getInt(auxiliary ? "bm21" : "bm20", 0);
|
||||
|
||||
imu.SetBiasGyroX(bias_g[0]);
|
||||
imu.SetBiasGyroY(bias_g[1]);
|
||||
imu.SetBiasGyroZ(bias_g[2]);
|
||||
|
||||
imu.SetBiasAccelX(bias_a[0]);
|
||||
imu.SetBiasAccelY(bias_a[1]);
|
||||
imu.SetBiasAccelZ(bias_a[2]);
|
||||
|
||||
imu.SetBiasCPassX(bias_m[0]);
|
||||
imu.SetBiasCPassY(bias_m[1]);
|
||||
imu.SetBiasCPassZ(bias_m[2]);
|
||||
|
||||
// Display both values from ESP32 and read back from IMU
|
||||
#ifdef DEBUG_SENSOR
|
||||
m_Logger.trace("On IMU and on ESP32 should match");
|
||||
m_Logger.trace("bias gyro on ESP32 : [%d, %d, %d]", bias_g[0], bias_g[1], bias_g[2]);
|
||||
m_Logger.trace("bias accel on ESP32: [%d, %d, %d]", bias_a[0], bias_a[1], bias_a[2]);
|
||||
m_Logger.trace("bias mag on ESP32 : [%d, %d, %d]", bias_m[0], bias_m[1], bias_m[2]);
|
||||
|
||||
imu.GetBiasGyroX(&bias_g[0]);
|
||||
imu.GetBiasGyroY(&bias_g[1]);
|
||||
imu.GetBiasGyroZ(&bias_g[2]);
|
||||
|
||||
imu.GetBiasAccelX(&bias_a[0]);
|
||||
imu.GetBiasAccelY(&bias_a[1]);
|
||||
imu.GetBiasAccelZ(&bias_a[2]);
|
||||
|
||||
imu.GetBiasCPassX(&bias_m[0]);
|
||||
imu.GetBiasCPassY(&bias_m[1]);
|
||||
imu.GetBiasCPassZ(&bias_m[2]);
|
||||
|
||||
m_Logger.trace("bias gyro on IMU : [%d, %d, %d]", bias_g[0], bias_g[1], bias_g[2]);
|
||||
m_Logger.trace("bias accel on IMU: [%d, %d, %d]", bias_a[0], bias_a[1], bias_a[2]);
|
||||
m_Logger.trace("bias mag on IMU : [%d, %d, %d]", bias_m[0], bias_m[1], bias_m[2]);
|
||||
#endif
|
||||
|
||||
#endif // #if esp8266 / elif ESP32
|
||||
|
||||
#if BIAS_DEBUG
|
||||
int bias_a[3], bias_g[3], bias_m[3];
|
||||
|
||||
imu.GetBiasGyroX(&bias_g[0]);
|
||||
imu.GetBiasGyroY(&bias_g[1]);
|
||||
imu.GetBiasGyroZ(&bias_g[2]);
|
||||
|
||||
imu.GetBiasAccelX(&bias_a[0]);
|
||||
imu.GetBiasAccelY(&bias_a[1]);
|
||||
imu.GetBiasAccelZ(&bias_a[2]);
|
||||
|
||||
imu.GetBiasCPassX(&bias_m[0]);
|
||||
imu.GetBiasCPassY(&bias_m[1]);
|
||||
imu.GetBiasCPassZ(&bias_m[2]);
|
||||
|
||||
m_Logger.trace("Starting Gyro Bias is %d, %d, %d", bias_g[0], bias_g[1], bias_g[2]);
|
||||
m_Logger.trace("Starting Accel Bias is %d, %d, %d", bias_a[0], bias_a[1], bias_a[2]);
|
||||
m_Logger.trace("Starting CPass Bias is %d, %d, %d", bias_m[0], bias_m[1], bias_m[2]);
|
||||
|
||||
//Sets all bias to 90
|
||||
bias_g[0] = 90;
|
||||
bias_g[1] = 90;
|
||||
bias_g[2] = 90;
|
||||
bias_a[0] = 90;
|
||||
bias_a[1] = 90;
|
||||
bias_a[2] = 90;
|
||||
bias_m[0] = 90;
|
||||
bias_m[1] = 90;
|
||||
bias_m[2] = 90;
|
||||
|
||||
//Sets all bias to 0 in memory
|
||||
imu.SetBiasGyroX(bias_g[0]);
|
||||
imu.SetBiasGyroY(bias_g[1]);
|
||||
imu.SetBiasGyroZ(bias_g[2]);
|
||||
|
||||
imu.SetBiasAccelX(bias_a[0]);
|
||||
imu.SetBiasAccelY(bias_a[1]);
|
||||
imu.SetBiasAccelZ(bias_a[2]);
|
||||
|
||||
imu.SetBiasCPassX(bias_m[0]);
|
||||
imu.SetBiasCPassY(bias_m[1]);
|
||||
imu.SetBiasCPassZ(bias_m[2]);
|
||||
|
||||
//Sets all bias to 0
|
||||
bias_g[0] = 0;
|
||||
bias_g[1] = 0;
|
||||
bias_g[2] = 0;
|
||||
bias_a[0] = 0;
|
||||
bias_a[1] = 0;
|
||||
bias_a[2] = 0;
|
||||
bias_m[0] = 0;
|
||||
bias_m[1] = 0;
|
||||
bias_m[2] = 0;
|
||||
|
||||
//Reloads all bias from memory
|
||||
imu.GetBiasGyroX(&bias_g[0]);
|
||||
imu.GetBiasGyroY(&bias_g[1]);
|
||||
imu.GetBiasGyroZ(&bias_g[2]);
|
||||
|
||||
imu.GetBiasAccelX(&bias_a[0]);
|
||||
imu.GetBiasAccelY(&bias_a[1]);
|
||||
imu.GetBiasAccelZ(&bias_a[2]);
|
||||
|
||||
imu.GetBiasCPassX(&bias_m[0]);
|
||||
imu.GetBiasCPassY(&bias_m[1]);
|
||||
imu.GetBiasCPassZ(&bias_m[2]);
|
||||
|
||||
m_Logger.trace("All set bias should be 90");
|
||||
|
||||
m_Logger.trace("Set Gyro Bias is %d, %d, %d", bias_g[0], bias_g[1], bias_g[2]);
|
||||
m_Logger.trace("Set Accel Bias is %d, %d, %d", bias_a[0], bias_a[1], bias_a[2]);
|
||||
m_Logger.trace("Set CPass Bias is %d, %d, %d", bias_m[0], bias_m[1], bias_m[2]);
|
||||
#endif // BIAS_DEBUG
|
||||
#endif // LOAD_BIAS
|
||||
}
|
||||
|
||||
void ICM20948Sensor::motionSetup() {
|
||||
#ifdef ESP32
|
||||
prefs.begin("ICM20948", false);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SENSOR
|
||||
#ifdef FULL_DEBUG
|
||||
imu.enableDebugging(Serial);
|
||||
#endif
|
||||
// SparkFun_ICM-20948_ArduinoLibrary only supports 0x68 or 0x69 via boolean, if something else throw a error
|
||||
@@ -482,8 +279,30 @@ void ICM20948Sensor::motionSetup() {
|
||||
m_Logger.fatal("Failed to reset FIFO");
|
||||
return;
|
||||
}
|
||||
|
||||
load_bias();
|
||||
|
||||
#if LOAD_BIAS
|
||||
// Initialize the configuration
|
||||
{
|
||||
SlimeVR::Configuration::CalibrationConfig sensorCalibration = configuration.getCalibration(sensorId);
|
||||
// If no compatible calibration data is found, the calibration data will just be zero-ed out
|
||||
switch (sensorCalibration.type) {
|
||||
case SlimeVR::Configuration::CalibrationConfigType::ICM20948:
|
||||
m_Calibration = sensorCalibration.data.icm20948;
|
||||
break;
|
||||
|
||||
case SlimeVR::Configuration::CalibrationConfigType::NONE:
|
||||
m_Logger.warn("No calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
break;
|
||||
|
||||
default:
|
||||
m_Logger.warn("Incompatible calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
load_bias();
|
||||
|
||||
lastData = millis();
|
||||
working = true;
|
||||
@@ -614,15 +433,7 @@ void ICM20948Sensor::sendData() {
|
||||
void ICM20948Sensor::startCalibration(int calibrationType) {
|
||||
// 20948 does continuous calibration
|
||||
|
||||
// If ESP32, manually force a new save
|
||||
#ifdef ESP32
|
||||
save_bias(false);
|
||||
#endif
|
||||
// If 8266, save the current bias values to eeprom
|
||||
#ifdef ESP8266
|
||||
// Types are int, device config saves float - need to save and load like mpu6050 does
|
||||
save_bias(false);
|
||||
#endif
|
||||
save_bias(false);
|
||||
}
|
||||
|
||||
//You need to override the library's initializeDMP to change some settings
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
#include <ICM_20948.h>
|
||||
#include "sensor.h"
|
||||
#include <arduino-timer.h> // Used for periodically saving bias
|
||||
#ifdef ESP32
|
||||
#include <Preferences.h> // ICM bias saving. ESP8266 use eprom
|
||||
#endif
|
||||
|
||||
class ICM20948Sensor : public Sensor
|
||||
{
|
||||
@@ -51,14 +48,11 @@ private:
|
||||
ICM_20948_Device_t pdev;
|
||||
icm_20948_DMP_data_t dmpData{};
|
||||
|
||||
SlimeVR::Configuration::ICM20948CalibrationConfig m_Calibration;
|
||||
|
||||
#define OVERRIDEDMPSETUP true
|
||||
#ifdef ESP32
|
||||
Preferences prefs;
|
||||
|
||||
Timer<> timer = timer_create_default();
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
Timer<> timer = timer_create_default();
|
||||
#endif
|
||||
// TapDetector tapDetector;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain
|
||||
Copyright (c) 2021 Eiren Rain & SlimeVR contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -33,13 +33,10 @@
|
||||
#include "network/network.h"
|
||||
#include <i2cscan.h>
|
||||
#include "calibration.h"
|
||||
#include "configuration.h"
|
||||
#include "GlobalVars.h"
|
||||
|
||||
void MPU6050Sensor::motionSetup()
|
||||
{
|
||||
//DeviceConfig * const config = getConfigPtr();
|
||||
|
||||
imu.initialize(addr);
|
||||
if (!imu.testConnection())
|
||||
{
|
||||
@@ -49,6 +46,28 @@ void MPU6050Sensor::motionSetup()
|
||||
|
||||
m_Logger.info("Connected to MPU6050 (0x%02x) at address 0x%02x", imu.getDeviceID(), addr);
|
||||
|
||||
#ifndef IMU_MPU6050_RUNTIME_CALIBRATION
|
||||
// Initialize the configuration
|
||||
{
|
||||
SlimeVR::Configuration::CalibrationConfig sensorCalibration = configuration.getCalibration(sensorId);
|
||||
// If no compatible calibration data is found, the calibration data will just be zero-ed out
|
||||
switch (sensorCalibration.type) {
|
||||
case SlimeVR::Configuration::CalibrationConfigType::MPU6050:
|
||||
m_Calibration = sensorCalibration.data.mpu6050;
|
||||
break;
|
||||
|
||||
case SlimeVR::Configuration::CalibrationConfigType::NONE:
|
||||
m_Logger.warn("No calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
break;
|
||||
|
||||
default:
|
||||
m_Logger.warn("Incompatible calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
devStatus = imu.dmpInitialize();
|
||||
|
||||
if (devStatus == 0)
|
||||
@@ -157,28 +176,30 @@ void MPU6050Sensor::startCalibration(int calibrationType) {
|
||||
|
||||
m_Logger.debug("Gathered baseline gyro reading");
|
||||
m_Logger.debug("Starting offset finder");
|
||||
DeviceConfig *const config = getConfigPtr();
|
||||
|
||||
switch (calibrationType)
|
||||
{
|
||||
case CALIBRATION_TYPE_INTERNAL_ACCEL:
|
||||
imu.CalibrateAccel(10);
|
||||
Network::sendCalibrationFinished(CALIBRATION_TYPE_INTERNAL_ACCEL, 0);//doesn't send calibration data anymore, has that been depricated in server?
|
||||
config->calibration->A_B[0] = imu.getXAccelOffset();
|
||||
config->calibration->A_B[1] = imu.getYAccelOffset();
|
||||
config->calibration->A_B[2] = imu.getZAccelOffset();
|
||||
saveConfig();
|
||||
m_Calibration.A_B[0] = imu.getXAccelOffset();
|
||||
m_Calibration.A_B[1] = imu.getYAccelOffset();
|
||||
m_Calibration.A_B[2] = imu.getZAccelOffset();
|
||||
break;
|
||||
case CALIBRATION_TYPE_INTERNAL_GYRO:
|
||||
imu.CalibrateGyro(10);
|
||||
Network::sendCalibrationFinished(CALIBRATION_TYPE_INTERNAL_GYRO, 0);//doesn't send calibration data anymore
|
||||
config->calibration->G_off[0] = imu.getXGyroOffset();
|
||||
config->calibration->G_off[1] = imu.getYGyroOffset();
|
||||
config->calibration->G_off[2] = imu.getZGyroOffset();
|
||||
saveConfig();
|
||||
m_Calibration.G_off[0] = imu.getXGyroOffset();
|
||||
m_Calibration.G_off[1] = imu.getYGyroOffset();
|
||||
m_Calibration.G_off[2] = imu.getZGyroOffset();
|
||||
break;
|
||||
}
|
||||
|
||||
SlimeVR::Configuration::CalibrationConfig calibration;
|
||||
calibration.type = SlimeVR::Configuration::CalibrationConfigType::MPU6050;
|
||||
calibration.data.mpu6050 = m_Calibration;
|
||||
configuration.setCalibration(sensorId, calibration);
|
||||
configuration.save();
|
||||
|
||||
m_Logger.info("Calibration finished");
|
||||
#endif // !IMU_MPU6050_RUNTIME_CALIBRATION
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ private:
|
||||
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
|
||||
|
||||
#ifndef IMU_MPU6050_RUNTIME_CALIBRATION
|
||||
SlimeVR::Configuration::MPU6050CalibrationConfig m_Calibration;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain, S.J. Remington, SlimeVR contributors
|
||||
Copyright (c) 2021 Eiren Rain, S.J. Remington & SlimeVR contributors
|
||||
|
||||
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
|
||||
@@ -26,8 +27,8 @@
|
||||
#include "calibration.h"
|
||||
#include "magneto1.4.h"
|
||||
#include "GlobalVars.h"
|
||||
// #include "mahony.h"
|
||||
// #include "madgwick.h"
|
||||
#include "mahony.h"
|
||||
#include "madgwick.h"
|
||||
#if not (defined(_MAHONY_H_) || defined(_MADGWICK_H_))
|
||||
#include "dmpmag.h"
|
||||
#endif
|
||||
@@ -38,8 +39,6 @@ constexpr float gscale = (250. / 32768.0) * (PI / 180.0); //gyro default 250 LSB
|
||||
#define MAG_CORR_RATIO 0.2
|
||||
|
||||
void MPU9250Sensor::motionSetup() {
|
||||
DeviceConfig * const config = getConfigPtr();
|
||||
calibration = &config->calibration[sensorId];
|
||||
// initialize device
|
||||
imu.initialize(addr);
|
||||
if(!imu.testConnection()) {
|
||||
@@ -68,6 +67,27 @@ void MPU9250Sensor::motionSetup() {
|
||||
startCalibration(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the configuration
|
||||
{
|
||||
SlimeVR::Configuration::CalibrationConfig sensorCalibration = configuration.getCalibration(sensorId);
|
||||
// If no compatible calibration data is found, the calibration data will just be zero-ed out
|
||||
switch (sensorCalibration.type) {
|
||||
case SlimeVR::Configuration::CalibrationConfigType::MPU9250:
|
||||
m_Calibration = sensorCalibration.data.mpu9250;
|
||||
break;
|
||||
|
||||
case SlimeVR::Configuration::CalibrationConfigType::NONE:
|
||||
m_Logger.warn("No calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
break;
|
||||
|
||||
default:
|
||||
m_Logger.warn("Incompatible calibration data found for sensor %d, ignoring...", sensorId);
|
||||
m_Logger.info("Calibration is advised");
|
||||
}
|
||||
}
|
||||
|
||||
#if not (defined(_MAHONY_H_) || defined(_MADGWICK_H_))
|
||||
devStatus = imu.dmpInitialize();
|
||||
if(devStatus == 0){
|
||||
@@ -170,9 +190,9 @@ void MPU9250Sensor::getMPUScaled()
|
||||
#if defined(_MAHONY_H_) || defined(_MADGWICK_H_)
|
||||
int16_t ax, ay, az, gx, gy, gz, mx, my, mz;
|
||||
imu.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
|
||||
Gxyz[0] = ((float)gx - calibration->G_off[0]) * gscale; //250 LSB(d/s) default to radians/s
|
||||
Gxyz[1] = ((float)gy - calibration->G_off[1]) * gscale;
|
||||
Gxyz[2] = ((float)gz - calibration->G_off[2]) * gscale;
|
||||
Gxyz[0] = ((float)gx - m_Calibration.G_off[0]) * gscale; //250 LSB(d/s) default to radians/s
|
||||
Gxyz[1] = ((float)gy - m_Calibration.G_off[1]) * gscale;
|
||||
Gxyz[2] = ((float)gz - m_Calibration.G_off[2]) * gscale;
|
||||
|
||||
Axyz[0] = (float)ax;
|
||||
Axyz[1] = (float)ay;
|
||||
@@ -181,13 +201,13 @@ void MPU9250Sensor::getMPUScaled()
|
||||
//apply offsets (bias) and scale factors from Magneto
|
||||
#if useFullCalibrationMatrix == true
|
||||
for (i = 0; i < 3; i++)
|
||||
temp[i] = (Axyz[i] - calibration->A_B[i]);
|
||||
Axyz[0] = calibration->A_Ainv[0][0] * temp[0] + calibration->A_Ainv[0][1] * temp[1] + calibration->A_Ainv[0][2] * temp[2];
|
||||
Axyz[1] = calibration->A_Ainv[1][0] * temp[0] + calibration->A_Ainv[1][1] * temp[1] + calibration->A_Ainv[1][2] * temp[2];
|
||||
Axyz[2] = calibration->A_Ainv[2][0] * temp[0] + calibration->A_Ainv[2][1] * temp[1] + calibration->A_Ainv[2][2] * temp[2];
|
||||
temp[i] = (Axyz[i] - m_Calibration.A_B[i]);
|
||||
Axyz[0] = m_Calibration.A_Ainv[0][0] * temp[0] + m_Calibration.A_Ainv[0][1] * temp[1] + m_Calibration.A_Ainv[0][2] * temp[2];
|
||||
Axyz[1] = m_Calibration.A_Ainv[1][0] * temp[0] + m_Calibration.A_Ainv[1][1] * temp[1] + m_Calibration.A_Ainv[1][2] * temp[2];
|
||||
Axyz[2] = m_Calibration.A_Ainv[2][0] * temp[0] + m_Calibration.A_Ainv[2][1] * temp[1] + m_Calibration.A_Ainv[2][2] * temp[2];
|
||||
#else
|
||||
for (i = 0; i < 3; i++)
|
||||
Axyz[i] = (Axyz[i] - calibration->A_B[i]);
|
||||
Axyz[i] = (Axyz[i] - m-Calibration.A_B[i]);
|
||||
#endif
|
||||
|
||||
#else
|
||||
@@ -205,13 +225,13 @@ void MPU9250Sensor::getMPUScaled()
|
||||
//apply offsets and scale factors from Magneto
|
||||
#if useFullCalibrationMatrix == true
|
||||
for (i = 0; i < 3; i++)
|
||||
temp[i] = (Mxyz[i] - calibration->M_B[i]);
|
||||
Mxyz[0] = calibration->M_Ainv[0][0] * temp[0] + calibration->M_Ainv[0][1] * temp[1] + calibration->M_Ainv[0][2] * temp[2];
|
||||
Mxyz[1] = calibration->M_Ainv[1][0] * temp[0] + calibration->M_Ainv[1][1] * temp[1] + calibration->M_Ainv[1][2] * temp[2];
|
||||
Mxyz[2] = calibration->M_Ainv[2][0] * temp[0] + calibration->M_Ainv[2][1] * temp[1] + calibration->M_Ainv[2][2] * temp[2];
|
||||
temp[i] = (Mxyz[i] - m_Calibration.M_B[i]);
|
||||
Mxyz[0] = m_Calibration.M_Ainv[0][0] * temp[0] + m_Calibration.M_Ainv[0][1] * temp[1] + m_Calibration.M_Ainv[0][2] * temp[2];
|
||||
Mxyz[1] = m_Calibration.M_Ainv[1][0] * temp[0] + m_Calibration.M_Ainv[1][1] * temp[1] + m_Calibration.M_Ainv[1][2] * temp[2];
|
||||
Mxyz[2] = m_Calibration.M_Ainv[2][0] * temp[0] + m_Calibration.M_Ainv[2][1] * temp[1] + m_Calibration.M_Ainv[2][2] * temp[2];
|
||||
#else
|
||||
for (i = 0; i < 3; i++)
|
||||
Mxyz[i] = (Mxyz[i] - calibration->M_B[i]);
|
||||
Mxyz[i] = (Mxyz[i] - m_Calibration.M_B[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -220,7 +240,7 @@ void MPU9250Sensor::startCalibration(int calibrationType) {
|
||||
#if not (defined(_MAHONY_H_) || defined(_MADGWICK_H_))
|
||||
// with DMP, we just need mag data
|
||||
constexpr int calibrationSamples = 300;
|
||||
DeviceConfig *config = getConfigPtr();
|
||||
|
||||
// Blink calibrating led before user should rotate the sensor
|
||||
m_Logger.info("Gently rotate the device while it's gathering magnetometer data");
|
||||
ledManager.pattern(15, 300, 3000/310);
|
||||
@@ -245,19 +265,18 @@ void MPU9250Sensor::startCalibration(int calibrationType) {
|
||||
m_Logger.debug("[INFO] Magnetometer calibration matrix:");
|
||||
m_Logger.debug("{");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
config->calibration[sensorId].M_B[i] = M_BAinv[0][i];
|
||||
config->calibration[sensorId].M_Ainv[0][i] = M_BAinv[1][i];
|
||||
config->calibration[sensorId].M_Ainv[1][i] = M_BAinv[2][i];
|
||||
config->calibration[sensorId].M_Ainv[2][i] = M_BAinv[3][i];
|
||||
m_Calibration.M_B[i] = M_BAinv[0][i];
|
||||
m_Calibration.M_Ainv[0][i] = M_BAinv[1][i];
|
||||
m_Calibration.M_Ainv[1][i] = M_BAinv[2][i];
|
||||
m_Calibration.M_Ainv[2][i] = M_BAinv[3][i];
|
||||
m_Logger.debug(" %f, %f, %f, %f", M_BAinv[0][i], M_BAinv[1][i], M_BAinv[2][i], M_BAinv[3][i]);
|
||||
}
|
||||
m_Logger.debug("}");
|
||||
|
||||
#elif
|
||||
#else
|
||||
|
||||
m_Logger.debug("Gathering raw data for device calibration...");
|
||||
constexpr int calibrationSamples = 300;
|
||||
DeviceConfig *config = getConfigPtr();
|
||||
// Reset values
|
||||
Gxyz[0] = 0;
|
||||
Gxyz[1] = 0;
|
||||
@@ -283,9 +302,9 @@ void MPU9250Sensor::startCalibration(int calibrationType) {
|
||||
#endif
|
||||
|
||||
Network::sendRawCalibrationData(Gxyz, CALIBRATION_TYPE_EXTERNAL_GYRO, 0);
|
||||
config->calibration[sensorId].G_off[0] = Gxyz[0];
|
||||
config->calibration[sensorId].G_off[1] = Gxyz[1];
|
||||
config->calibration[sensorId].G_off[2] = Gxyz[2];
|
||||
m_Calibration.G_off[0] = Gxyz[0];
|
||||
m_Calibration.G_off[1] = Gxyz[1];
|
||||
m_Calibration.G_off[2] = Gxyz[2];
|
||||
|
||||
// Blink calibrating led before user should rotate the sensor
|
||||
m_Logger.info("Gently rotate the device while it's gathering accelerometer and magnetometer data");
|
||||
@@ -320,29 +339,36 @@ void MPU9250Sensor::startCalibration(int calibrationType) {
|
||||
m_Logger.debug("{");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
config->calibration[sensorId].A_B[i] = A_BAinv[0][i];
|
||||
config->calibration[sensorId].A_Ainv[0][i] = A_BAinv[1][i];
|
||||
config->calibration[sensorId].A_Ainv[1][i] = A_BAinv[2][i];
|
||||
config->calibration[sensorId].A_Ainv[2][i] = A_BAinv[3][i];
|
||||
m_Calibration.A_B[i] = A_BAinv[0][i];
|
||||
m_Calibration.A_Ainv[0][i] = A_BAinv[1][i];
|
||||
m_Calibration.A_Ainv[1][i] = A_BAinv[2][i];
|
||||
m_Calibration.A_Ainv[2][i] = A_BAinv[3][i];
|
||||
m_Logger.debug(" %f, %f, %f, %f", A_BAinv[0][i], A_BAinv[1][i], A_BAinv[2][i], A_BAinv[3][i]);
|
||||
}
|
||||
m_Logger.debug("}");
|
||||
m_Logger.debug("[INFO] Magnetometer calibration matrix:");
|
||||
m_Logger.debug("{");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
config->calibration[sensorId].M_B[i] = M_BAinv[0][i];
|
||||
config->calibration[sensorId].M_Ainv[0][i] = M_BAinv[1][i];
|
||||
config->calibration[sensorId].M_Ainv[1][i] = M_BAinv[2][i];
|
||||
config->calibration[sensorId].M_Ainv[2][i] = M_BAinv[3][i];
|
||||
m_Calibration.M_B[i] = M_BAinv[0][i];
|
||||
m_Calibration.M_Ainv[0][i] = M_BAinv[1][i];
|
||||
m_Calibration.M_Ainv[1][i] = M_BAinv[2][i];
|
||||
m_Calibration.M_Ainv[2][i] = M_BAinv[3][i];
|
||||
m_Logger.debug(" %f, %f, %f, %f", M_BAinv[0][i], M_BAinv[1][i], M_BAinv[2][i], M_BAinv[3][i]);
|
||||
}
|
||||
m_Logger.debug("}");
|
||||
#endif
|
||||
|
||||
m_Logger.debug("Now Saving EEPROM");
|
||||
setConfig(*config);
|
||||
m_Logger.debug("Saving the calibration data");
|
||||
|
||||
SlimeVR::Configuration::CalibrationConfig calibration;
|
||||
calibration.type = SlimeVR::Configuration::CalibrationConfigType::MPU9250;
|
||||
calibration.data.mpu9250 = m_Calibration;
|
||||
configuration.setCalibration(sensorId, calibration);
|
||||
configuration.save();
|
||||
|
||||
ledManager.off();
|
||||
Network::sendCalibrationFinished(CALIBRATION_TYPE_EXTERNAL_ALL, 0);
|
||||
m_Logger.debug("Finished Saving EEPROM");
|
||||
m_Logger.debug("Saved the calibration data");
|
||||
|
||||
m_Logger.info("Calibration data gathered");
|
||||
}
|
||||
@@ -41,7 +41,6 @@ public:
|
||||
|
||||
private:
|
||||
MPU9250 imu{};
|
||||
CalibrationConfig *calibration;
|
||||
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)
|
||||
@@ -59,6 +58,8 @@ private:
|
||||
// Loop timing globals
|
||||
unsigned long now = 0, last = 0; // micros() timers
|
||||
float deltat = 0; // loop time in seconds
|
||||
|
||||
SlimeVR::Configuration::MPU9250CalibrationConfig m_Calibration;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <Arduino.h>
|
||||
#include <quat.h>
|
||||
#include <vector3.h>
|
||||
#include "configuration.h"
|
||||
#include "configuration/Configuration.h"
|
||||
#include "globals.h"
|
||||
#include "logging/Logger.h"
|
||||
#include "utils.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2021 Eiren Rain
|
||||
Copyright (c) 2021 Eiren Rain & SlimeVR contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "network/network.h"
|
||||
#include "logging/Logger.h"
|
||||
#include <CmdCallback.hpp>
|
||||
#include <EEPROM.h>
|
||||
#include "GlobalVars.h"
|
||||
|
||||
namespace SerialCommands {
|
||||
SlimeVR::Logging::Logger logger("SerialCommands");
|
||||
@@ -73,9 +73,9 @@ namespace SerialCommands {
|
||||
|
||||
void cmdFactoryReset(CmdParser * parser) {
|
||||
logger.info("FACTORY RESET");
|
||||
for (int i = 0; i <= 4096; i++) // Clear EEPROM
|
||||
EEPROM.write(i, 0xFF);
|
||||
EEPROM.commit();
|
||||
|
||||
configuration.reset();
|
||||
|
||||
WiFi.disconnect(true); // Clear WiFi credentials
|
||||
#if ESP8266
|
||||
ESP.eraseConfig(); // Clear ESP config
|
||||
|
||||
24
src/utils.h
24
src/utils.h
@@ -1,7 +1,31 @@
|
||||
/*
|
||||
SlimeVR Code is placed under the MIT license
|
||||
Copyright (c) 2022 TheDevMinerTV
|
||||
|
||||
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 UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#define UNPACK_VECTOR(V) V.x, V.y, V.z
|
||||
#define UNPACK_VECTOR_ARRAY(V) V[0], V[1], V[2]
|
||||
#define UNPACK_QUATERNION(Q) Q.x, Q.y, Q.z, Q.w
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
#include <Wire.h>
|
||||
|
||||
#include "ota.h"
|
||||
#include "configuration.h"
|
||||
#include "configuration/Configuration.h"
|
||||
#include "helper_3dmath.h"
|
||||
#include "udpclient.h"
|
||||
#include "credentials.h"
|
||||
|
||||
DeviceConfig config;
|
||||
SlimeVR::Configuration::DeviceConfig config;
|
||||
|
||||
uint8_t portArray[] = {16, 5, 4, 0, 2, 14, 12, 13};
|
||||
//String portMap[] = {"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7"}; //for Wemos
|
||||
|
||||
Reference in New Issue
Block a user