From 51c7d15a8b3a01d642051c134040595cfa68273c Mon Sep 17 00:00:00 2001 From: Meia <71262281+kounocom@users.noreply.github.com> Date: Sat, 19 Jul 2025 14:00:38 +0200 Subject: [PATCH 01/25] Add IMU_USE_EXTERNAL_CLOCK to debug.h (#464) * Add IMU_USE_EXTERNAL_CLOCK to debug.h * Move it to globals.h actually --- src/globals.h | 4 ++++ src/sensors/softfusion/drivers/icm45686.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/globals.h b/src/globals.h index 2198cc7..d7fdfdc 100644 --- a/src/globals.h +++ b/src/globals.h @@ -53,6 +53,10 @@ #define EXPERIMENTAL_BNO_DISABLE_ACCEL_CALIBRATION true #endif +#ifndef IMU_USE_EXTERNAL_CLOCK +#define IMU_USE_EXTERNAL_CLOCK true // Use external clock for IMU (ICM-45686 only) +#endif + #ifndef VENDOR_NAME #define VENDOR_NAME "Unknown" #endif diff --git a/src/sensors/softfusion/drivers/icm45686.h b/src/sensors/softfusion/drivers/icm45686.h index f2ef5ad..43cb152 100644 --- a/src/sensors/softfusion/drivers/icm45686.h +++ b/src/sensors/softfusion/drivers/icm45686.h @@ -75,8 +75,10 @@ struct ICM45686 : public ICM45Base { bool initialize() { ICM45Base::softResetIMU(); +#if IMU_USE_EXTERNAL_CLOCK m_RegisterInterface.writeReg(Regs::Pin9Config::reg, Regs::Pin9Config::value); m_RegisterInterface.writeReg(Regs::RtcConfig::reg, Regs::RtcConfig::value); +#endif return ICM45Base::initializeBase(); } }; From a8e689784f8108c05d68a208fbaa895d71232d50 Mon Sep 17 00:00:00 2001 From: Chris D <32441086+CheesecakeCG@users.noreply.github.com> Date: Tue, 12 Aug 2025 05:01:00 -0400 Subject: [PATCH 02/25] Update BMI160 bulk read function (#466) --- src/sensors/softfusion/drivers/bmi160.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sensors/softfusion/drivers/bmi160.h b/src/sensors/softfusion/drivers/bmi160.h index 22493af..1f79be1 100644 --- a/src/sensors/softfusion/drivers/bmi160.h +++ b/src/sensors/softfusion/drivers/bmi160.h @@ -30,6 +30,7 @@ #include #include "../../../sensorinterface/RegisterInterface.h" +#include "callbacks.h" #include "vqf.h" namespace SlimeVR::Sensors::SoftFusion::Drivers { @@ -182,12 +183,7 @@ struct BMI160 { return to_ret; } - template - void bulkRead( - AccelCall&& processAccelSample, - GyroCall&& processGyroSample, - TempCall&& processTempSample - ) { + void bulkRead(DriverCallbacks&& callbacks) { const auto fifo_bytes = m_RegisterInterface.readReg16(Regs::FifoLength) & 0x7FF; const auto bytes_to_read = std::min( @@ -218,7 +214,7 @@ struct BMI160 { gyro[0] = getFromFifo(i, read_buffer); gyro[1] = getFromFifo(i, read_buffer); gyro[2] = getFromFifo(i, read_buffer); - processGyroSample(gyro, GyrTs); + callbacks.processGyroSample(gyro, GyrTs); } if (header & Fifo::AccelDataBit) { @@ -226,7 +222,7 @@ struct BMI160 { accel[0] = getFromFifo(i, read_buffer); accel[1] = getFromFifo(i, read_buffer); accel[2] = getFromFifo(i, read_buffer); - processAccelSample(accel, AccTs); + callbacks.processAccelSample(accel, AccTs); } } } From ba1da6054b3bb87cd906146976c927e97220903c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 19:02:16 +0300 Subject: [PATCH 03/25] Bump actions/checkout from 4 to 5 (#469) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/actions.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index cc4c40a..39bcdcd 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -14,7 +14,7 @@ jobs: format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: jidicula/clang-format-action@v4.14.0 with: clang-format-version: "17" @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/cache@v4 with: path: | From 1a7619e2e66c67689e70c981b50d5dedb29df7bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:04:02 +0300 Subject: [PATCH 04/25] Bump actions/setup-python from 5 to 6 (#470) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/actions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 39bcdcd..64b90c9 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -45,7 +45,7 @@ jobs: run: git fetch --tags origin --recurse-submodules=no --force - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.12" From 23390ddb3993da3a72de81bb24d02e12849c5f53 Mon Sep 17 00:00:00 2001 From: gorbit99 Date: Wed, 17 Sep 2025 17:03:09 +0200 Subject: [PATCH 05/25] Rework WiFi code (#435) * Rework WiFi code * Add missing trySavedAttempt() * Remap status values to new failure values * Skip saved credentials properly --- src/GlobalVars.h | 4 + src/main.cpp | 2 + src/network/manager.cpp | 6 +- src/network/wifihandler.cpp | 412 ++++++++++++++++++------------- src/network/wifihandler.h | 84 +++++-- src/network/wifiprovisioning.cpp | 18 +- src/network/wifiprovisioning.h | 25 +- src/serial/serialcommands.cpp | 16 +- 8 files changed, 352 insertions(+), 215 deletions(-) diff --git a/src/GlobalVars.h b/src/GlobalVars.h index 4c7cb3c..6ee0196 100644 --- a/src/GlobalVars.h +++ b/src/GlobalVars.h @@ -28,6 +28,8 @@ #include "configuration/Configuration.h" #include "network/connection.h" #include "network/manager.h" +#include "network/wifihandler.h" +#include "network/wifiprovisioning.h" #include "sensors/SensorManager.h" #include "status/LEDManager.h" #include "status/StatusManager.h" @@ -40,3 +42,5 @@ extern SlimeVR::Sensors::SensorManager sensorManager; extern SlimeVR::Network::Manager networkManager; extern SlimeVR::Network::Connection networkConnection; extern BatteryMonitor battery; +extern SlimeVR::WiFiNetwork wifiNetwork; +extern SlimeVR::WifiProvisioning wifiProvisioning; diff --git a/src/main.cpp b/src/main.cpp index 706cb02..d8589cd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,8 @@ SlimeVR::Status::StatusManager statusManager; SlimeVR::Configuration::Configuration configuration; SlimeVR::Network::Manager networkManager; SlimeVR::Network::Connection networkConnection; +SlimeVR::WiFiNetwork wifiNetwork; +SlimeVR::WifiProvisioning wifiProvisioning; #if DEBUG_MEASURE_SENSOR_TIME_TAKEN SlimeVR::Debugging::TimeTakenMeasurer sensorMeasurer{"Sensors"}; diff --git a/src/network/manager.cpp b/src/network/manager.cpp index 70477d7..e945bb4 100644 --- a/src/network/manager.cpp +++ b/src/network/manager.cpp @@ -26,14 +26,14 @@ namespace SlimeVR::Network { -void Manager::setup() { ::WiFiNetwork::setUp(); } +void Manager::setup() { wifiNetwork.setUp(); } void Manager::update() { - WiFiNetwork::upkeep(); + wifiNetwork.upkeep(); auto wasConnected = m_IsConnected; - m_IsConnected = ::WiFiNetwork::isConnected(); + m_IsConnected = wifiNetwork.isConnected(); if (!m_IsConnected) { return; diff --git a/src/network/wifihandler.cpp b/src/network/wifihandler.cpp index b258138..e9fb434 100644 --- a/src/network/wifihandler.cpp +++ b/src/network/wifihandler.cpp @@ -22,29 +22,20 @@ */ #include "GlobalVars.h" #include "globals.h" -#include "logging/Logger.h" #if !ESP8266 #include "esp_wifi.h" #endif -unsigned long lastWifiReportTime = 0; -unsigned long wifiConnectionTimeout = millis(); -bool isWifiConnected = false; -uint8_t wifiState = SLIME_WIFI_NOT_SETUP; -bool hadWifi = false; -unsigned long last_rssi_sample = 0; +namespace SlimeVR { -// TODO: Cleanup with proper classes -SlimeVR::Logging::Logger wifiHandlerLogger("WiFiHandler"); - -void reportWifiError() { +void WiFiNetwork::reportWifiProgress() { if (lastWifiReportTime + 1000 < millis()) { lastWifiReportTime = millis(); Serial.print("."); } } -void setStaticIPIfDefined() { +void WiFiNetwork::setStaticIPIfDefined() { #ifdef WIFI_USE_STATICIP const IPAddress ip(WIFI_STATIC_IP); const IPAddress gateway(WIFI_STATIC_GATEWAY); @@ -53,43 +44,35 @@ void setStaticIPIfDefined() { #endif } -bool WiFiNetwork::isConnected() { return isWifiConnected; } +bool WiFiNetwork::isConnected() const { + return wifiState == WiFiReconnectionStatus::Success; +} void WiFiNetwork::setWiFiCredentials(const char* SSID, const char* pass) { - stopProvisioning(); - setStaticIPIfDefined(); - WiFi.begin(SSID, pass); + wifiProvisioning.stopProvisioning(); + WiFi.persistent(true); + tryConnecting(false, SSID, pass); + retriedOnG = false; // Reset state, will get back into provisioning if can't connect hadWifi = false; - wifiState = SLIME_WIFI_SERVER_CRED_ATTEMPT; - wifiConnectionTimeout = millis(); + wifiState = WiFiReconnectionStatus::ServerCredAttempt; } IPAddress WiFiNetwork::getAddress() { return WiFi.localIP(); } void WiFiNetwork::setUp() { + // Don't need to save the already saved credentials or the hardcoded ones + WiFi.persistent(false); wifiHandlerLogger.info("Setting up WiFi"); - WiFi.persistent(true); WiFi.mode(WIFI_STA); -#if ESP8266 -#if USE_ATTENUATION - WiFi.setOutputPower(20.0 - ATTENUATION_N); -#endif - WiFi.setPhyMode(WIFI_PHY_MODE_11N); -#endif WiFi.hostname("SlimeVR FBT Tracker"); wifiHandlerLogger.info( "Loaded credentials for SSID '%s' and pass length %d", WiFi.SSID().c_str(), WiFi.psk().length() ); - setStaticIPIfDefined(); - wl_status_t status = WiFi.begin( - ); // Should connect to last used access point, see - // https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/station-class.html#begin - wifiHandlerLogger.debug("Status: %d", status); - wifiState = SLIME_WIFI_SAVED_ATTEMPT; - wifiConnectionTimeout = millis(); + + trySavedCredentials(); #if ESP8266 #if POWERSAVING_MODE == POWER_SAVING_NONE @@ -121,156 +104,249 @@ void WiFiNetwork::setUp() { #endif } -void onConnected() { - WiFiNetwork::stopProvisioning(); +void WiFiNetwork::onConnected() { + wifiState = WiFiReconnectionStatus::Success; + wifiProvisioning.stopProvisioning(); statusManager.setStatus(SlimeVR::Status::WIFI_CONNECTING, false); - isWifiConnected = true; hadWifi = true; wifiHandlerLogger.info( "Connected successfully to SSID '%s', IP address %s", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str() ); + // Reset it, in case we just connected with server creds + WiFi.persistent(false); } -uint8_t WiFiNetwork::getWiFiState() { return wifiState; } +WiFiNetwork::WiFiReconnectionStatus WiFiNetwork::getWiFiState() { return wifiState; } void WiFiNetwork::upkeep() { - upkeepProvisioning(); - if (WiFi.status() != WL_CONNECTED) { - if (isWifiConnected) { - wifiHandlerLogger.warn("Connection to WiFi lost, reconnecting..."); - isWifiConnected = false; + wifiProvisioning.upkeepProvisioning(); + if (WiFi.status() == WL_CONNECTED) { + if (!isConnected()) { + onConnected(); + return; } - statusManager.setStatus(SlimeVR::Status::WIFI_CONNECTING, true); - reportWifiError(); - if (wifiConnectionTimeout + 11000 < millis()) { - switch (wifiState) { - case SLIME_WIFI_NOT_SETUP: // Wasn't set up - return; - case SLIME_WIFI_SAVED_ATTEMPT: // Couldn't connect with first set of - // credentials -#if ESP8266 - // Try again but with 11G but only if there are credentials, - // otherwise we just waste time before switching to hardcoded - // credentials. - if (WiFi.SSID().length() > 0) { -#if USE_ATTENUATION - WiFi.setOutputPower(20.0 - ATTENUATION_G); -#endif - WiFi.setPhyMode(WIFI_PHY_MODE_11G); - setStaticIPIfDefined(); - WiFi.begin(); - wifiConnectionTimeout = millis(); - wifiHandlerLogger.error( - "Can't connect from saved credentials, status: %d.", - WiFi.status() - ); - wifiHandlerLogger.debug( - "Trying saved credentials with PHY Mode G..." - ); - } else { - wifiHandlerLogger.debug( - "Skipping PHY Mode G attempt on 0-length SSID..." - ); - } -#endif - wifiState = SLIME_WIFI_SAVED_G_ATTEMPT; - return; - case SLIME_WIFI_SAVED_G_ATTEMPT: // Couldn't connect with first set of - // credentials with PHY Mode G -#if defined(WIFI_CREDS_SSID) && defined(WIFI_CREDS_PASSWD) - // Try hardcoded credentials now -#if ESP8266 -#if USE_ATTENUATION - WiFi.setOutputPower(20.0 - ATTENUATION_N); -#endif - WiFi.setPhyMode(WIFI_PHY_MODE_11N); -#endif - setStaticIPIfDefined(); - WiFi.begin(WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); - wifiConnectionTimeout = millis(); - wifiHandlerLogger.error( - "Can't connect from saved credentials, status: %d.", - WiFi.status() - ); - wifiHandlerLogger.debug("Trying hardcoded credentials..."); -#endif - wifiState = SLIME_WIFI_HARDCODE_ATTEMPT; - return; - case SLIME_WIFI_HARDCODE_ATTEMPT: // Couldn't connect with second set - // of credentials -#if defined(WIFI_CREDS_SSID) && defined(WIFI_CREDS_PASSWD) && ESP8266 - // Try hardcoded credentials again, - // but with PHY Mode G -#if USE_ATTENUATION - WiFi.setOutputPower(20.0 - ATTENUATION_G); -#endif - WiFi.setPhyMode(WIFI_PHY_MODE_11G); - setStaticIPIfDefined(); - WiFi.begin(WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); - wifiConnectionTimeout = millis(); - wifiHandlerLogger.error( - "Can't connect from saved credentials, status: %d.", - WiFi.status() - ); - wifiHandlerLogger.debug( - "Trying hardcoded credentials with WiFi PHY Mode G..." - ); -#endif - wifiState = SLIME_WIFI_HARDCODE_G_ATTEMPT; - return; - case SLIME_WIFI_SERVER_CRED_ATTEMPT: // Couldn't connect with - // server-sent credentials. -#if ESP8266 - // Try again silently but with 11G -#if USE_ATTENUATION - WiFi.setOutputPower(20.0 - ATTENUATION_G); -#endif - WiFi.setPhyMode(WIFI_PHY_MODE_11G); - setStaticIPIfDefined(); - WiFi.begin(); - wifiConnectionTimeout = millis(); - wifiState = SLIME_WIFI_SERVER_CRED_G_ATTEMPT; -#endif - return; - case SLIME_WIFI_HARDCODE_G_ATTEMPT: // Couldn't connect with second set - // of credentials with PHY Mode G. - case SLIME_WIFI_SERVER_CRED_G_ATTEMPT: // Or if couldn't connect with - // server-sent credentials -// Return to the default PHY Mode N. -#if ESP8266 -#if USE_ATTENUATION - WiFi.setOutputPower(20.0 - ATTENUATION_N); -#endif - WiFi.setPhyMode(WIFI_PHY_MODE_11N); -#endif - // Start smart config - if (!hadWifi && !WiFi.smartConfigDone() - && wifiConnectionTimeout + 11000 < millis()) { - if (WiFi.status() != WL_IDLE_STATUS) { - wifiHandlerLogger.error( - "Can't connect from any credentials, status: %d.", - WiFi.status() - ); - wifiConnectionTimeout = millis(); - } - startProvisioning(); - } - return; - } - } - return; - } - if (!isWifiConnected) { - onConnected(); - return; - } else { - if (millis() - last_rssi_sample >= 2000) { - last_rssi_sample = millis(); + + if (millis() - lastRssiSample >= 2000) { + lastRssiSample = millis(); uint8_t signalStrength = WiFi.RSSI(); networkConnection.sendSignalStrength(signalStrength); } + return; + } + + if (isConnected()) { + statusManager.setStatus(SlimeVR::Status::WIFI_CONNECTING, true); + wifiHandlerLogger.warn("Connection to WiFi lost, reconnecting..."); + trySavedCredentials(); + return; + } + + reportWifiProgress(); + if (millis() - wifiConnectionTimeout + < static_cast(WiFiTimeoutSeconds * 1000) + && WiFi.status() == WL_DISCONNECTED) { + return; + } + + switch (wifiState) { + case WiFiReconnectionStatus::NotSetup: // Wasn't set up + return; + case WiFiReconnectionStatus::SavedAttempt: // Couldn't connect with + // first set of + // credentials + if (!trySavedCredentials()) { + tryHardcodedCredentials(); + } + return; + case WiFiReconnectionStatus::HardcodeAttempt: // Couldn't connect with + // second set of credentials + if (!tryHardcodedCredentials()) { + wifiState = WiFiReconnectionStatus::Failed; + } + return; + case WiFiReconnectionStatus::ServerCredAttempt: // Couldn't connect with + // server-sent credentials. + if (!tryServerCredentials()) { + wifiState = WiFiReconnectionStatus::Failed; + } + return; + case WiFiReconnectionStatus::Failed: // Couldn't connect with second set of + // credentials or server credentials +// Return to the default PHY Mode N. +#if ESP8266 + if constexpr (USE_ATTENUATION) { + WiFi.setOutputPower(20.0 - ATTENUATION_N); + } + WiFi.setPhyMode(WIFI_PHY_MODE_11N); +#endif + // Start smart config + if (!hadWifi && !WiFi.smartConfigDone() + && millis() - wifiConnectionTimeout + >= static_cast(WiFiTimeoutSeconds * 1000)) { + if (WiFi.status() != WL_IDLE_STATUS) { + wifiHandlerLogger.error( + "Can't connect from any credentials, error: %d, reason: %s.", + static_cast(statusToFailure(WiFi.status())), + statusToReasonString(WiFi.status()) + ); + wifiConnectionTimeout = millis(); + } + wifiProvisioning.startProvisioning(); + } + return; } - return; } + +const char* WiFiNetwork::statusToReasonString(wl_status_t status) { + switch (status) { + case WL_DISCONNECTED: + return "Timeout"; +#ifdef ESP8266 + case WL_WRONG_PASSWORD: + return "Wrong password"; + case WL_CONNECT_FAILED: + return "Connection failed"; +#elif ESP32 + case WL_CONNECT_FAILED: + return "Wrong password"; +#endif + + case WL_NO_SSID_AVAIL: + return "SSID not found"; + default: + return "Unknown"; + } +} + +WiFiNetwork::WiFiFailureReason WiFiNetwork::statusToFailure(wl_status_t status) { + switch (status) { + case WL_DISCONNECTED: + return WiFiFailureReason::Timeout; +#ifdef ESP8266 + case WL_WRONG_PASSWORD: + return WiFiFailureReason::WrongPassword; +#elif ESP32 + case WL_CONNECT_FAILED: + return WiFiFailureReason::WrongPassword; +#endif + + case WL_NO_SSID_AVAIL: + return WiFiFailureReason::SSIDNotFound; + default: + return WiFiFailureReason::Unknown; + } +} + +void WiFiNetwork::showConnectionAttemptFailed(const char* type) const { + wifiHandlerLogger.error( + "Can't connect from %s credentials, error: %d, reason: %s.", + type, + static_cast(statusToFailure(WiFi.status())), + statusToReasonString(WiFi.status()) + ); +} + +bool WiFiNetwork::trySavedCredentials() { + if (WiFi.SSID().length() == 0) { + wifiHandlerLogger.debug("Skipping saved credentials attempt on 0-length SSID..." + ); + wifiState = WiFiReconnectionStatus::HardcodeAttempt; + return false; + } + + if (wifiState == WiFiReconnectionStatus::SavedAttempt) { + showConnectionAttemptFailed("saved"); + + if (WiFi.status() != WL_DISCONNECTED) { + return false; + } + + if (retriedOnG) { + return false; + } + + retriedOnG = true; + wifiHandlerLogger.debug("Trying saved credentials with PHY Mode G..."); + return tryConnecting(true); + } + + retriedOnG = false; + + wifiState = WiFiReconnectionStatus::SavedAttempt; + return tryConnecting(); +} + +bool WiFiNetwork::tryHardcodedCredentials() { +#if defined(WIFI_CREDS_SSID) && defined(WIFI_CREDS_PASSWD) + if (wifiState == WiFiReconnectionStatus::HardcodeAttempt) { + showConnectionAttemptFailed("hardcoded"); + + if (WiFi.status() != WL_DISCONNECTED) { + return false; + } + + if (retriedOnG) { + return false; + } + + retriedOnG = true; + wifiHandlerLogger.debug("Trying hardcoded credentials with PHY Mode G..."); + return tryConnecting(true, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); + } + + retriedOnG = false; + + wifiState = WiFiReconnectionStatus::HardcodeAttempt; + return tryConnecting(false, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); +#else + wifiState = WiFiReconnectionStatus::HardcodeAttempt; + return false; +#endif +} + +bool WiFiNetwork::tryServerCredentials() { + if (WiFi.status() != WL_DISCONNECTED) { + return false; + } + + if (retriedOnG) { + return false; + } + + retriedOnG = true; + + return tryConnecting(true); +} + +bool WiFiNetwork::tryConnecting(bool phyModeG, const char* SSID, const char* pass) { +#if ESP8266 + if (phyModeG) { + WiFi.setPhyMode(WIFI_PHY_MODE_11G); + if constexpr (USE_ATTENUATION) { + WiFi.setOutputPower(20.0 - ATTENUATION_G); + } + } else { + WiFi.setPhyMode(WIFI_PHY_MODE_11N); + if constexpr (USE_ATTENUATION) { + WiFi.setOutputPower(20.0 - ATTENUATION_N); + } + } +#else + if (phyModeG) { + return false; + } +#endif + + setStaticIPIfDefined(); + if (SSID == nullptr) { + WiFi.begin(); + } else { + WiFi.begin(SSID, pass); + } + wifiConnectionTimeout = millis(); + return true; +} + +} // namespace SlimeVR diff --git a/src/network/wifihandler.h b/src/network/wifihandler.h index eeb3b3d..ac18479 100644 --- a/src/network/wifihandler.h +++ b/src/network/wifihandler.h @@ -20,33 +20,75 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef SLIMEVR_WIFI_H_ -#define SLIMEVR_WIFI_H_ +#pragma once +#include "logging/Logger.h" #ifdef ESP8266 #include #else #include #endif -namespace WiFiNetwork { -bool isConnected(); -void setUp(); -void upkeep(); -void setWiFiCredentials(const char* SSID, const char* pass); -IPAddress getAddress(); -uint8_t getWiFiState(); -} // namespace WiFiNetwork +namespace SlimeVR { + +class WiFiNetwork { +public: + enum class WiFiReconnectionStatus { + NotSetup = 0, + SavedAttempt, + HardcodeAttempt, + ServerCredAttempt, + Failed, + Success + }; + + enum class WiFiFailureReason { + Timeout = 0, + SSIDNotFound = 1, + WrongPassword = 2, + Unknown = 3, + }; + + [[nodiscard]] bool isConnected() const; + void setUp(); + void upkeep(); + void setWiFiCredentials(const char* SSID, const char* pass); + static IPAddress getAddress(); + WiFiReconnectionStatus getWiFiState(); + +private: + static constexpr float WiFiTimeoutSeconds = 11; + + void reportWifiProgress(); + void setStaticIPIfDefined(); + void onConnected(); + + bool trySavedCredentials(); + bool tryHardcodedCredentials(); + bool tryServerCredentials(); + bool tryConnecting( + bool phyModeG = false, + const char* SSID = nullptr, + const char* pass = nullptr + ); + + void showConnectionAttemptFailed(const char* type) const; + + static const char* statusToReasonString(wl_status_t status); + static WiFiFailureReason statusToFailure(wl_status_t status); + + unsigned long lastWifiReportTime = 0; + unsigned long wifiConnectionTimeout = millis(); + bool isWifiConnected = false; + WiFiReconnectionStatus wifiState = WiFiReconnectionStatus::NotSetup; + bool retriedOnG = false; + bool hadWifi = false; + unsigned long lastRssiSample = 0; + + uint8_t lastFailStatus = 0; + + SlimeVR::Logging::Logger wifiHandlerLogger{"WiFiHandler"}; +}; /** Wifi Reconnection Statuses **/ -typedef enum { - SLIME_WIFI_NOT_SETUP = 0, - SLIME_WIFI_SAVED_ATTEMPT, - SLIME_WIFI_SAVED_G_ATTEMPT, - SLIME_WIFI_HARDCODE_ATTEMPT, - SLIME_WIFI_HARDCODE_G_ATTEMPT, - SLIME_WIFI_SERVER_CRED_ATTEMPT, - SLIME_WIFI_SERVER_CRED_G_ATTEMPT -} wifi_reconnection_statuses; - -#endif // SLIMEVR_WIFI_H_ +} // namespace SlimeVR diff --git a/src/network/wifiprovisioning.cpp b/src/network/wifiprovisioning.cpp index ad3f954..7fce74c 100644 --- a/src/network/wifiprovisioning.cpp +++ b/src/network/wifiprovisioning.cpp @@ -29,29 +29,31 @@ // it sucks. // TODO: New implementation: https://github.com/SlimeVR/SlimeVR-Tracker-ESP/issues/71 -// TODO: Cleanup with proper classes -SlimeVR::Logging::Logger wifiProvisioningLogger("WiFiProvisioning"); -bool provisioning = false; +namespace SlimeVR { -void WiFiNetwork::upkeepProvisioning() { +void WifiProvisioning::upkeepProvisioning() { // Called even when not provisioning to do things like provide neighbours or other // upkeep } -void WiFiNetwork::startProvisioning() { +void WifiProvisioning::startProvisioning() { if (WiFi.beginSmartConfig()) { provisioning = true; wifiProvisioningLogger.info("SmartConfig started"); } } -void WiFiNetwork::stopProvisioning() { +void WifiProvisioning::stopProvisioning() { WiFi.stopSmartConfig(); provisioning = false; } -void WiFiNetwork::provideNeighbours() { +void WifiProvisioning::provideNeighbours() { // TODO: SmartConfig can't do this, created for future } -bool WiFiNetwork::isProvisioning() { return provisioning && !WiFi.smartConfigDone(); } +bool WifiProvisioning::isProvisioning() const { + return provisioning && !WiFi.smartConfigDone(); +} + +} // namespace SlimeVR diff --git a/src/network/wifiprovisioning.h b/src/network/wifiprovisioning.h index 46ce53c..ac9c5a1 100644 --- a/src/network/wifiprovisioning.h +++ b/src/network/wifiprovisioning.h @@ -23,12 +23,23 @@ #ifndef SLIMEVR_WIFIPROVISIONING_H_ #define SLIMEVR_WIFIPROVISIONING_H_ -namespace WiFiNetwork { -void upkeepProvisioning(); -void startProvisioning(); -void stopProvisioning(); -bool isProvisioning(); -void provideNeighbours(); -} // namespace WiFiNetwork +#include "logging/Logger.h" + +namespace SlimeVR { + +class WifiProvisioning { +public: + void upkeepProvisioning(); + void startProvisioning(); + void stopProvisioning(); + bool isProvisioning() const; + void provideNeighbours(); + +private: + SlimeVR::Logging::Logger wifiProvisioningLogger{"WiFiProvisioning"}; + bool provisioning = false; +}; + +} // namespace SlimeVR #endif // SLIMEVR_WIFIPROVISIONING_H_ diff --git a/src/serial/serialcommands.cpp b/src/serial/serialcommands.cpp index 4d5f537..72e4e65 100644 --- a/src/serial/serialcommands.cpp +++ b/src/serial/serialcommands.cpp @@ -85,7 +85,7 @@ void cmdSet(CmdParser* parser) { return; } - WiFiNetwork::setWiFiCredentials(sc_ssid, sc_pw); + wifiNetwork.setWiFiCredentials(sc_ssid, sc_pw); logger.info("CMD SET WIFI OK: New wifi credentials set, reconnecting"); } } else if (parser->equalCmdParam(1, "BWIFI")) { @@ -131,7 +131,7 @@ void cmdSet(CmdParser* parser) { // set the pointer for pass to null for no password ppass = NULL; } - WiFiNetwork::setWiFiCredentials(ssid, ppass); + wifiNetwork.setWiFiCredentials(ssid, ppass); logger.info("CMD SET BWIFI OK: New wifi credentials set, reconnecting"); } } else { @@ -150,10 +150,10 @@ void printState() { HARDWARE_MCU, PROTOCOL_VERSION, FIRMWARE_VERSION, - WiFiNetwork::getAddress().toString().c_str(), + wifiNetwork.getAddress().toString().c_str(), WiFi.macAddress().c_str(), statusManager.getStatus(), - WiFiNetwork::getWiFiState() + wifiNetwork.getWiFiState() ); char vendorBuffer[512]; @@ -311,10 +311,10 @@ void cmdGet(CmdParser* parser) { HARDWARE_MCU, PROTOCOL_VERSION, FIRMWARE_VERSION, - WiFiNetwork::getAddress().toString().c_str(), + wifiNetwork.getAddress().toString().c_str(), WiFi.macAddress().c_str(), statusManager.getStatus(), - WiFiNetwork::getWiFiState() + wifiNetwork.getWiFiState() ); auto& sensor0 = sensorManager.getSensors()[0]; sensor0->motionLoop(); @@ -347,8 +347,8 @@ void cmdGet(CmdParser* parser) { if (WiFi.status() != WL_CONNECTED) { WiFi.disconnect(); } - if (WiFiNetwork::isProvisioning()) { - WiFiNetwork::stopProvisioning(); + if (wifiProvisioning.isProvisioning()) { + wifiProvisioning.stopProvisioning(); } WiFi.scanNetworks(); From c07319f37f7966b1aedab59476c7c55c23bdac25 Mon Sep 17 00:00:00 2001 From: JLitewski Date: Wed, 17 Sep 2025 11:06:09 -0400 Subject: [PATCH 06/25] Cleanup I2Cscan library (#459) * Cleanup scanI2C library * Fixed formatting issues * First round of changes based on feedback * Fixed a fat fingered mistake * Actually fix the formatting issues --- lib/i2cscan/i2cscan.cpp | 209 +++++++++++++++++++--------------- lib/i2cscan/i2cscan.h | 4 +- src/debug.h | 2 + src/serial/serialcommands.cpp | 21 +++- 4 files changed, 142 insertions(+), 94 deletions(-) diff --git a/lib/i2cscan/i2cscan.cpp b/lib/i2cscan/i2cscan.cpp index 2eda286..55a7d0f 100644 --- a/lib/i2cscan/i2cscan.cpp +++ b/lib/i2cscan/i2cscan.cpp @@ -1,116 +1,156 @@ #include "i2cscan.h" + +#include +#include +#include + #include "../../src/globals.h" +#include "../../src/consts.h" -#ifdef ESP8266 -uint8_t portArray[] = {16, 5, 4, 2, 14, 12, 13}; -uint8_t portExclude[] = {LED_PIN}; -String portMap[] = {"D0", "D1", "D2", "D4", "D5", "D6", "D7"}; -#elif defined(ESP32C3) -uint8_t portArray[] = {2, 3, 4, 5, 6, 7, 8, 9, 10}; -uint8_t portExclude[] = {18, 19, 20, 21, LED_PIN}; -String portMap[] = {"2", "3", "4", "5", "6", "7", "8", "9", "10"}; -#elif defined(ESP32C6) -uint8_t portArray[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 18, 19, 20, 21, 22, 23}; -String portMap[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "14", "15", "18", "19", "20", "21", "22", "23"}; -uint8_t portExclude[] = {12, 13, 16, 17, LED_PIN}; -#elif defined(ESP32) -uint8_t portArray[] = {4, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33}; -String portMap[] = {"4", "13", "14", "15", "16", "17", "18", "19", "21", "22", "23", "25", "26", "27", "32", "33"}; -uint8_t portExclude[] = {LED_PIN}; -#endif - -namespace I2CSCAN -{ - enum class ScanState { +namespace I2CSCAN { + enum class ScanState : uint8_t { IDLE, SCANNING, DONE }; - ScanState scanState = ScanState::IDLE; - uint8_t currentSDA = 0; - uint8_t currentSCL = 0; - uint8_t currentAddress = 1; - bool found = false; - std::vector validPorts; + namespace { + ScanState scanState = ScanState::IDLE; + uint8_t currentSDA = 0; + uint8_t currentSCL = 0; + uint8_t currentAddress = 1; + bool found = false; + uint8_t txFails = 0; + std::vector validPorts; - void scani2cports() - { +#ifdef ESP8266 + std::array portArray = {16, 5, 4, 2, 14, 12, 13}; + std::array portMap = {"D0", "D1", "D2", "D4", "D5", "D6", "D7"}; + std::array portExclude = {LED_PIN}; +#elif defined(ESP32C3) + std::array portArray = {2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::array portMap = {"2", "3", "4", "5", "6", "7", "8", "9", "10"}; + std::array portExclude = {18, 19, 20, 21, LED_PIN}; +#elif defined(ESP32C6) + std::array portArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 18, 19, 20, 21, 22, 23}; + std::array portMap = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "14", "15", "18", "19", "20", "21", "22", "23"}; + std::array portExclude = {12, 13, 16, 17, LED_PIN}; +#elif defined(ESP32) + std::array portArray = {4, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33}; + std::array portMap = {"4", "13", "14", "15", "16", "17", "18", "19", "21", "22", "23", "25", "26", "27", "32", "33"}; + std::array portExclude = {LED_PIN}; +#endif + + bool selectNextPort() { + currentSCL++; + + if(validPorts[currentSCL] == validPorts[currentSDA]) currentSCL++; + + if (currentSCL < validPorts.size()) { + Wire.begin((int)validPorts[currentSDA], (int)validPorts[currentSCL]); //NOLINT + return true; + } + + currentSCL = 0; + currentSDA++; + + if (currentSDA >= validPorts.size()) { + if (!found) { + Serial.println("[ERROR] I2C: No I2C devices found"); //NOLINT + } + #ifdef ESP32 + Wire.end(); + #endif + Wire.begin(static_cast(PIN_IMU_SDA), static_cast(PIN_IMU_SCL)); + scanState = ScanState::DONE; + return false; + } + + Wire.begin((int)validPorts[currentSDA], (int)validPorts[currentSCL]); + return true; + } + template + uint8_t countCommonElements( + const std::array& array1, + const std::array& array2) { + + uint8_t count = 0; + for (const auto& elem1 : array1) { + for (const auto& elem2 : array2) { + if (elem1 == elem2) { + count++; + } + } + } + + return count; + } + } // anonymous namespace + + void scani2cports() { if (scanState != ScanState::IDLE) { - return; + if (scanState == ScanState::DONE) { + Serial.println("[DEBUG] I2C scan finished previously, resetting and scanning again..."); //NOLINT + } else { + return; // Already scanning, do not start again + } } // Filter out excluded ports - for (size_t i = 0; i < sizeof(portArray); i++) { - if (!inArray(portArray[i], portExclude, sizeof(portExclude))) { - validPorts.push_back(portArray[i]); - } - } + validPorts.clear(); + uint8_t excludes = countCommonElements(portArray, portExclude); + validPorts.reserve(portArray.size() - excludes); // Reserve space to avoid reallocations + for (const auto& port : portArray) { + if (std::find(portExclude.begin(), portExclude.end(), port) == portExclude.end()) { + validPorts.push_back(port); // Port is valid, add it to the list + } + } + + // Reset scan variables and start scanning found = false; currentSDA = 0; currentSCL = 1; currentAddress = 1; + txFails = 0; scanState = ScanState::SCANNING; - } + } - bool selectNextPort() { - currentSCL++; - if(validPorts[currentSCL] == validPorts[currentSDA]) - currentSCL++; - if (currentSCL < validPorts.size()) { - Wire.begin((int)validPorts[currentSDA], (int)validPorts[currentSCL]); - return true; - } - - currentSCL = 0; - currentSDA++; - - if (currentSDA >= validPorts.size()) { - if (!found) { - Serial.println("[ERR] I2C: No I2C devices found"); - } -#ifdef ESP32 - Wire.end(); -#endif - Wire.begin(static_cast(PIN_IMU_SDA), static_cast(PIN_IMU_SCL)); - scanState = ScanState::DONE; - return false; - } - - Wire.begin((int)validPorts[currentSDA], (int)validPorts[currentSCL]); - return true; - } - - void update() - { + void update() { if (scanState != ScanState::SCANNING) { return; } - if (currentAddress == 1) { #ifdef ESP32 + if (currentAddress == 1) { Wire.end(); + } #endif - } Wire.beginTransmission(currentAddress); - byte error = Wire.endTransmission(); + const uint8_t error = Wire.endTransmission(); - if (error == 0) - { - Serial.printf("[DBG] I2C (@ %s(%d) : %s(%d)): I2C device found at address 0x%02x !\n", + if (error == 0) { + Serial.printf("[INFO ] I2C (@ %s(%d) : %s(%d)): I2C device found at address 0x%02x!\n", portMap[currentSDA].c_str(), validPorts[currentSDA], portMap[currentSCL].c_str(), validPorts[currentSCL], currentAddress); found = true; - } - else if (error == 4) - { - Serial.printf("[ERR] I2C (@ %s(%d) : %s(%d)): Unknown error at address 0x%02x\n", + } else if (error == 4) { // Unable to start transaction, log and warn + Serial.printf("[WARN ] I2C (@ %s(%d) : %s(%d)): Unable to start transaction at address 0x%02x!\n", portMap[currentSDA].c_str(), validPorts[currentSDA], portMap[currentSCL].c_str(), validPorts[currentSCL], currentAddress); + txFails++; } currentAddress++; + if (currentAddress <= 127) { + if (txFails > 5) { +#if BOARD == BOARD_SLIMEVR_LEGACY || BOARD == BOARD_SLIMEVR_DEV || BOARD == BOARD_SLIMEVR || BOARD == BOARD_SLIMEVR_V1_2 + Serial.printf("[ERROR] I2C: Too many transaction errors (%d), please power off the tracker and contact SlimeVR support!\n", txFails); +#else + Serial.printf("[ERROR] I2C: Too many transaction errors (%d), please power off the tracker and check the IMU connections!\n", txFails); +#endif + } + return; } @@ -118,19 +158,6 @@ namespace I2CSCAN selectNextPort(); } - bool inArray(uint8_t value, uint8_t* array, size_t arraySize) - { - for (size_t i = 0; i < arraySize; i++) - { - if (value == array[i]) - { - return true; - } - } - - return false; - } - bool hasDevOnBus(uint8_t addr) { byte error; #if ESP32C3 @@ -165,9 +192,9 @@ namespace I2CSCAN */ int clearBus(uint8_t SDA, uint8_t SCL) { - #if defined(TWCR) && defined(TWEN) +#if defined(TWCR) && defined(TWEN) TWCR &= ~(_BV(TWEN)); // Disable the Atmel 2-Wire interface so we can control the SDA and SCL pins directly - #endif +#endif pinMode(SDA, INPUT_PULLUP); pinMode(SCL, INPUT_PULLUP); diff --git a/lib/i2cscan/i2cscan.h b/lib/i2cscan/i2cscan.h index 3fa2eaf..d658e55 100644 --- a/lib/i2cscan/i2cscan.h +++ b/lib/i2cscan/i2cscan.h @@ -11,7 +11,7 @@ namespace I2CSCAN { bool hasDevOnBus(uint8_t addr); uint8_t pickDevice(uint8_t addr1, uint8_t addr2, bool scanIfNotFound); int clearBus(uint8_t SDA, uint8_t SCL); - boolean inArray(uint8_t value, uint8_t* arr, size_t arrSize); + bool inArray(uint8_t value, const uint8_t *array, size_t arraySize); } -#endif // _I2CSCAN_H_ \ No newline at end of file +#endif // _I2CSCAN_H_ diff --git a/src/debug.h b/src/debug.h index 307eea7..0e85a5d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -39,6 +39,8 @@ // disable if problems. Server does nothing with value so disabled atm #define SEND_ACCELERATION true // send linear acceleration to the server +#define EXT_SERIAL_COMMANDS false // Set to true to enable extra serial debug commands + // Debug information #define LOG_LEVEL LOG_LEVEL_DEBUG diff --git a/src/serial/serialcommands.cpp b/src/serial/serialcommands.cpp index 72e4e65..124c7df 100644 --- a/src/serial/serialcommands.cpp +++ b/src/serial/serialcommands.cpp @@ -35,10 +35,19 @@ #include "nvs_flash.h" #endif +#ifdef EXT_SERIAL_COMMANDS +#define CALLBACK_SIZE 7 // Increase callback size to allow for debug commands +#include "i2cscan.h" +#endif + +#ifndef CALLBACK_SIZE +#define CALLBACK_SIZE 6 // Default callback size +#endif + namespace SerialCommands { SlimeVR::Logging::Logger logger("SerialCommands"); -CmdCallback<6> cmdCallbacks; +CmdCallback cmdCallbacks; CmdParser cmdParser; CmdBuffer<256> cmdBuffer; @@ -457,6 +466,13 @@ void cmdDeleteCalibration(CmdParser* parser) { configuration.eraseSensors(); } +#if EXT_SERIAL_COMMANDS +void cmdScanI2C(CmdParser* parser) { + logger.info("Forcing I2C scan..."); + I2CSCAN::scani2cports(); +} +#endif + void setUp() { cmdCallbacks.addCmd("SET", &cmdSet); cmdCallbacks.addCmd("GET", &cmdGet); @@ -464,6 +480,9 @@ void setUp() { cmdCallbacks.addCmd("REBOOT", &cmdReboot); cmdCallbacks.addCmd("DELCAL", &cmdDeleteCalibration); cmdCallbacks.addCmd("TCAL", &cmdTemperatureCalibration); +#if EXT_SERIAL_COMMANDS + cmdCallbacks.addCmd("SCANI2C", &cmdScanI2C); +#endif } void update() { cmdCallbacks.updateCmdProcessing(&cmdParser, &cmdBuffer, &Serial); } From 1ddbcd48559f40ddf2acb4a4ef05f5c01035d4c9 Mon Sep 17 00:00:00 2001 From: Summer <71572746+SummerSigh@users.noreply.github.com> Date: Thu, 18 Sep 2025 06:07:15 -0700 Subject: [PATCH 07/25] reserve positional packet (#471) --- src/network/packets.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/packets.h b/src/network/packets.h index fcff747..55a6973 100644 --- a/src/network/packets.h +++ b/src/network/packets.h @@ -54,6 +54,7 @@ enum class SendPacketType : uint8_t { // RotationAcceleration = 23, AcknowledgeConfigChange = 24, FlexData = 26, + // PositionData = 27, Bundle = 100, Inspection = 105, }; From 96b6b7acec87845725f1746e7a3968c7b72a1c12 Mon Sep 17 00:00:00 2001 From: Meia <71262281+kounocom@users.noreply.github.com> Date: Mon, 29 Sep 2025 19:55:48 +0300 Subject: [PATCH 08/25] Optimized VQFParams 2: Electric Boogaloo (#472) * Adjust IMU parameters * Format * Forgot a comment * Use v2 optimized VQFParams GLOBALLY * Always calibrate sensor timesteps on startup * Formatting --------- Co-authored-by: gorbit99 --- lib/vqf/vqf.h | 22 ++++++------- src/sensors/softfusion/drivers/bmi160.h | 15 +++------ src/sensors/softfusion/drivers/bmi270.h | 14 +++----- src/sensors/softfusion/drivers/icm42688.h | 14 +++----- src/sensors/softfusion/drivers/icm45605.h | 10 ++---- src/sensors/softfusion/drivers/icm45686.h | 16 ++-------- src/sensors/softfusion/drivers/icm45base.h | 6 ++-- src/sensors/softfusion/drivers/lsm6ds3trc.h | 32 +++++++++---------- src/sensors/softfusion/drivers/lsm6dso.h | 18 ++++------- src/sensors/softfusion/drivers/lsm6dsr.h | 20 ++++-------- src/sensors/softfusion/drivers/lsm6dsv.h | 20 ++++-------- src/sensors/softfusion/drivers/mpu6050.h | 8 +---- .../runtimecalibration/RuntimeCalibration.h | 22 ++++++------- 13 files changed, 77 insertions(+), 140 deletions(-) diff --git a/lib/vqf/vqf.h b/lib/vqf/vqf.h index bde7cd0..dad1a78 100644 --- a/lib/vqf/vqf.h +++ b/lib/vqf/vqf.h @@ -56,7 +56,7 @@ struct VQFParams { * * Default value: 3.0 s */ - vqf_real_t tauAcc = 3.0; + vqf_real_t tauAcc = 4.337983; /** * @brief Time constant \f$\tau_\mathrm{mag}\f$ for magnetometer update in seconds. * @@ -106,7 +106,7 @@ struct VQFParams { * * Default value: 0.5 °/s */ - vqf_real_t biasSigmaInit = 0.5; + vqf_real_t biasSigmaInit = 3.219453; /** * @brief Time in which the bias estimation uncertainty increases from 0 °/s to 0.1 * °/s (in seconds). @@ -115,7 +115,7 @@ struct VQFParams { * * Default value: 100.0 s */ - vqf_real_t biasForgettingTime = 100.0; + vqf_real_t biasForgettingTime = 136.579346; /** * @brief Maximum expected gyroscope bias (in degrees per second). * @@ -126,7 +126,7 @@ struct VQFParams { * * Default value: 2.0 °/s */ - vqf_real_t biasClip = 2.0; + vqf_real_t biasClip = 5.0; #ifndef VQF_NO_MOTION_BIAS_ESTIMATION /** * @brief Standard deviation of the converged bias estimation uncertainty during @@ -137,7 +137,7 @@ struct VQFParams { * * Default value: 0.1 °/s */ - vqf_real_t biasSigmaMotion = 0.1; + vqf_real_t biasSigmaMotion = 0.348501; /** * @brief Forgetting factor for unobservable bias in vertical direction during * motion. @@ -149,7 +149,7 @@ struct VQFParams { * * Default value: 0.0001 */ - vqf_real_t biasVerticalForgettingFactor = 0.0001; + vqf_real_t biasVerticalForgettingFactor = 0.007056; #endif /** * @brief Standard deviation of the converged bias estimation uncertainty during @@ -160,7 +160,7 @@ struct VQFParams { * * Default value: 0.03 ° */ - vqf_real_t biasSigmaRest = 0.03; + vqf_real_t biasSigmaRest = 0.063616; /** * @brief Time threshold for rest detection (in seconds). @@ -170,7 +170,7 @@ struct VQFParams { * * Default value: 1.5 s */ - vqf_real_t restMinT = 1.5; + vqf_real_t restMinT = 2.586910; /** * @brief Time constant for the low-pass filter used in rest detection (in seconds). * @@ -179,7 +179,7 @@ struct VQFParams { * * Default value: 0.5 s */ - vqf_real_t restFilterTau = 0.5; + vqf_real_t restFilterTau = 1.114532; /** * @brief Angular velocity threshold for rest detection (in °/s). * @@ -189,7 +189,7 @@ struct VQFParams { * * Default value: 2.0 °/s */ - vqf_real_t restThGyr = 2.0; + vqf_real_t restThGyr = 1.399189; /** * @brief Acceleration threshold for rest detection (in m/s²). * @@ -198,7 +198,7 @@ struct VQFParams { * * Default value: 0.5 m/s² */ - vqf_real_t restThAcc = 0.5; + vqf_real_t restThAcc = 1.418598; /** * @brief Time constant for current norm/dip value in magnetic disturbance detection diff --git a/src/sensors/softfusion/drivers/bmi160.h b/src/sensors/softfusion/drivers/bmi160.h index 1f79be1..d41553c 100644 --- a/src/sensors/softfusion/drivers/bmi160.h +++ b/src/sensors/softfusion/drivers/bmi160.h @@ -37,7 +37,7 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { // Driver uses acceleration range at 4G // and gyroscope range at 1000DPS -// Gyroscope ODR = 400Hz, accel ODR = 100Hz +// Gyroscope ODR = 200Hz, accel ODR = 100Hz // Timestamps reading are not used // Sensorhub to be implemented @@ -47,7 +47,7 @@ struct BMI160 { static constexpr auto Name = "BMI160"; static constexpr auto Type = SensorTypeID::BMI160; - static constexpr float GyrTs = 1.0 / 400.0; + static constexpr float GyrTs = 1.0 / 200.0; static constexpr float AccTs = 1.0 / 100.0; static constexpr float MagTs = 1.0 / 100; @@ -58,14 +58,7 @@ struct BMI160 { static constexpr float TemperatureZROChange = 2.0f; // wow maybe BMI270 isn't that bad actually - static constexpr VQFParams SensorVQFParams{ - // need to be refined, this IMU sucks - .motionBiasEstEnabled = true, - .biasSigmaInit = 0.5f, - .biasClip = 2.0f, - .restThGyr = 0.5f, - .restThAcc = 0.196f, - }; + static constexpr VQFParams SensorVQFParams{}; RegisterInterface& m_RegisterInterface; SlimeVR::Logging::Logger& m_Logger; @@ -102,7 +95,7 @@ struct BMI160 { struct GyrConf { static constexpr uint8_t reg = 0x42; - static constexpr uint8_t value = 0b0101010; // 400Hz, filter mode normal + static constexpr uint8_t value = 0b0101001; // 200Hz, filter mode normal }; struct GyrRange { diff --git a/src/sensors/softfusion/drivers/bmi270.h b/src/sensors/softfusion/drivers/bmi270.h index bdc66e8..eabea7a 100644 --- a/src/sensors/softfusion/drivers/bmi270.h +++ b/src/sensors/softfusion/drivers/bmi270.h @@ -38,7 +38,7 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { // Driver uses acceleration range at 16g // and gyroscope range at 1000dps -// Gyroscope ODR = 400Hz, accel ODR = 100Hz +// Gyroscope ODR = 200Hz, accel ODR = 100Hz // Timestamps reading are not used struct BMI270 { @@ -46,7 +46,7 @@ struct BMI270 { static constexpr auto Name = "BMI270"; static constexpr auto Type = SensorTypeID::BMI270; - static constexpr float GyrTs = 1.0 / 400.0; + static constexpr float GyrTs = 1.0 / 200.0; static constexpr float AccTs = 1.0 / 100.0; static constexpr float MagTs = 1.0 / 100; @@ -56,13 +56,7 @@ struct BMI270 { static constexpr float TemperatureZROChange = 6.667f; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 0.5f, - .biasClip = 1.0f, - .restThGyr = 0.5f, - .restThAcc = 0.196f, - }; + static constexpr VQFParams SensorVQFParams{}; struct MotionlessCalibrationData { bool valid; @@ -138,7 +132,7 @@ struct BMI270 { static constexpr uint8_t filterHighPerfMode = 1 << 7; static constexpr uint8_t value - = rate400Hz | DLPFModeNorm | noisePerfMode | filterHighPerfMode; + = rate200Hz | DLPFModeNorm | noisePerfMode | filterHighPerfMode; }; struct GyrRange { diff --git a/src/sensors/softfusion/drivers/icm42688.h b/src/sensors/softfusion/drivers/icm42688.h index 61ec437..25fa1df 100644 --- a/src/sensors/softfusion/drivers/icm42688.h +++ b/src/sensors/softfusion/drivers/icm42688.h @@ -34,7 +34,7 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { // Driver uses acceleration range at 8g // and gyroscope range at 1000dps -// Gyroscope ODR = 500Hz, accel ODR = 100Hz +// Gyroscope ODR = 200Hz, accel ODR = 100Hz // Timestamps reading not used, as they're useless (constant predefined increment) struct ICM42688 { @@ -42,7 +42,7 @@ struct ICM42688 { static constexpr auto Name = "ICM-42688"; static constexpr auto Type = SensorTypeID::ICM42688; - static constexpr float GyrTs = 1.0 / 500.0; + static constexpr float GyrTs = 1.0 / 200.0; static constexpr float AccTs = 1.0 / 100.0; static constexpr float TempTs = 1.0 / 500.0; @@ -56,13 +56,7 @@ struct ICM42688 { static constexpr float TemperatureZROChange = 20.0f; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 0.5f, - .biasClip = 1.0f, - .restThGyr = 0.5f, - .restThAcc = 0.196f, - }; + static constexpr VQFParams SensorVQFParams{}; RegisterInterface& m_RegisterInterface; SlimeVR::Logging::Logger& m_Logger; @@ -99,7 +93,7 @@ struct ICM42688 { struct GyroConfig { static constexpr uint8_t reg = 0x4f; static constexpr uint8_t value - = (0b001 << 5) | 0b1111; // 1000dps, odr=500Hz + = (0b001 << 5) | 0b0111; // 1000dps, odr=200Hz }; struct AccelConfig { static constexpr uint8_t reg = 0x50; diff --git a/src/sensors/softfusion/drivers/icm45605.h b/src/sensors/softfusion/drivers/icm45605.h index 2b7f0d0..29659d0 100644 --- a/src/sensors/softfusion/drivers/icm45605.h +++ b/src/sensors/softfusion/drivers/icm45605.h @@ -32,20 +32,14 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { // and gyroscope range at 4000dps // using high resolution mode // Uses 32.768kHz clock -// Gyroscope ODR = 409.6Hz, accel ODR = 204.8Hz +// Gyroscope ODR = 204.8Hz, accel ODR = 102.4Hz // Timestamps reading not used, as they're useless (constant predefined increment) struct ICM45605 : public ICM45Base { static constexpr auto Name = "ICM-45605"; static constexpr auto Type = SensorTypeID::ICM45605; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 0.3f, - .biasClip = 0.6f, - .restThGyr = 0.3f, - .restThAcc = 0.0098f, - }; + static constexpr VQFParams SensorVQFParams{}; ICM45605(RegisterInterface& registerInterface, SlimeVR::Logging::Logger& logger) : ICM45Base{registerInterface, logger} {} diff --git a/src/sensors/softfusion/drivers/icm45686.h b/src/sensors/softfusion/drivers/icm45686.h index 43cb152..0c5f867 100644 --- a/src/sensors/softfusion/drivers/icm45686.h +++ b/src/sensors/softfusion/drivers/icm45686.h @@ -32,26 +32,14 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { // and gyroscope range at 4000dps // using high resolution mode // Uses 32.768kHz clock -// Gyroscope ODR = 409.6Hz, accel ODR = 102.4Hz +// Gyroscope ODR = 204.8Hz, accel ODR = 102.4Hz // Timestamps reading not used, as they're useless (constant predefined increment) struct ICM45686 : public ICM45Base { static constexpr auto Name = "ICM-45686"; static constexpr auto Type = SensorTypeID::ICM45686; - static constexpr VQFParams SensorVQFParams{ - .tauAcc = 7.171490, - .biasSigmaInit = 0.337976, - .biasForgettingTime = 352.235500, - .biasClip = 5.0, - .biasSigmaMotion = 0.985346, - .biasVerticalForgettingFactor = 0.007959, - .biasSigmaRest = 0.028897, - .restMinT = 4.648680, - .restFilterTau = 1.900166, - .restThGyr = 2.620598, - .restThAcc = 2.142593, - }; + static constexpr VQFParams SensorVQFParams{}; ICM45686(RegisterInterface& registerInterface, SlimeVR::Logging::Logger& logger) : ICM45Base{registerInterface, logger} {} diff --git a/src/sensors/softfusion/drivers/icm45base.h b/src/sensors/softfusion/drivers/icm45base.h index de08e4f..977eac4 100644 --- a/src/sensors/softfusion/drivers/icm45base.h +++ b/src/sensors/softfusion/drivers/icm45base.h @@ -34,13 +34,13 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { // and gyroscope range at 4000dps // using high resolution mode // Uses 32.768kHz clock -// Gyroscope ODR = 409.6Hz, accel ODR = 102.4Hz +// Gyroscope ODR = 204.8Hz, accel ODR = 102.4Hz // Timestamps reading not used, as they're useless (constant predefined increment) struct ICM45Base { static constexpr uint8_t Address = 0x68; - static constexpr float GyrTs = 1.0 / 409.6; + static constexpr float GyrTs = 1.0 / 204.8; static constexpr float AccTs = 1.0 / 102.4; static constexpr float TempTs = 1.0 / 409.6; @@ -71,7 +71,7 @@ struct ICM45Base { struct GyroConfig { static constexpr uint8_t reg = 0x1c; static constexpr uint8_t value - = (0b0000 << 4) | 0b0111; // 4000dps, odr=409.6Hz + = (0b0000 << 4) | 0b1000; // 4000dps, odr=204.8Hz }; struct AccelConfig { diff --git a/src/sensors/softfusion/drivers/lsm6ds3trc.h b/src/sensors/softfusion/drivers/lsm6ds3trc.h index 6d44895..db2e79b 100644 --- a/src/sensors/softfusion/drivers/lsm6ds3trc.h +++ b/src/sensors/softfusion/drivers/lsm6ds3trc.h @@ -33,21 +33,25 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { -// Driver uses acceleration range at 8g +// Driver uses acceleration range at 4g // and gyroscope range at 1000dps -// Gyroscope ODR = 416Hz, accel ODR = 416Hz +// Gyroscope ODR = 208Hz, accel ODR = 104Hz struct LSM6DS3TRC { static constexpr uint8_t Address = 0x6a; static constexpr auto Name = "LSM6DS3TR-C"; static constexpr auto Type = SensorTypeID::LSM6DS3TRC; - static constexpr float Freq = 416; + static constexpr float GyrFreq = 208.0f; + static constexpr float AccFreq = 104.0f; + static constexpr float MagFreq = 100.0f; + static constexpr float TempFreq + = 416.0f; // I guess it's just output at the FIFO ODR? - static constexpr float GyrTs = 1.0 / Freq; - static constexpr float AccTs = 1.0 / Freq; - static constexpr float MagTs = 1.0 / Freq; - static constexpr float TempTs = 1.0 / Freq; + static constexpr float GyrTs = 1.0 / GyrFreq; + static constexpr float AccTs = 1.0 / AccFreq; + static constexpr float MagTs = 1.0 / MagFreq; + static constexpr float TempTs = 1.0 / TempFreq; static constexpr float GyroSensitivity = 28.571428571f; static constexpr float AccelSensitivity = 4098.360655738f; @@ -57,13 +61,7 @@ struct LSM6DS3TRC { static constexpr float TemperatureZROChange = 2.0f; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 3.0f, - .biasClip = 6.0f, - .restThGyr = 3.0f, - .restThAcc = 0.392f, - }; + static constexpr VQFParams SensorVQFParams{}; RegisterInterface& m_RegisterInterface; SlimeVR::Logging::Logger m_Logger; @@ -79,12 +77,12 @@ struct LSM6DS3TRC { }; struct Ctrl1XL { static constexpr uint8_t reg = 0x10; - static constexpr uint8_t value = (0b11 << 2) | (0b0110 << 4); // 8g, 416Hz + static constexpr uint8_t value = (0b10 << 2) | (0b0100 << 4); // 4g, 104Hz }; struct Ctrl2G { static constexpr uint8_t reg = 0x11; static constexpr uint8_t value - = (0b10 << 2) | (0b0110 << 4); // 1000dps, 416Hz + = (0b10 << 2) | (0b0101 << 4); // 1000dps, 208Hz }; struct Ctrl3C { static constexpr uint8_t reg = 0x12; @@ -104,7 +102,7 @@ struct LSM6DS3TRC { struct FifoCtrl5 { static constexpr uint8_t reg = 0x0a; static constexpr uint8_t value - = 0b110 | (0b0111 << 3); // continuous mode, odr = 833Hz + = 0b110 | (0b0110 << 3); // continuous mode, odr = 416Hz }; static constexpr uint8_t FifoStatus = 0x3a; diff --git a/src/sensors/softfusion/drivers/lsm6dso.h b/src/sensors/softfusion/drivers/lsm6dso.h index eafc11b..4ab490d 100644 --- a/src/sensors/softfusion/drivers/lsm6dso.h +++ b/src/sensors/softfusion/drivers/lsm6dso.h @@ -33,7 +33,7 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { -// Driver uses acceleration range at 8g +// Driver uses acceleration range at 4g // and gyroscope range at 1000dps // Gyroscope ODR = 416Hz, accel ODR = 104Hz @@ -42,7 +42,7 @@ struct LSM6DSO : LSM6DSOutputHandler { static constexpr auto Name = "LSM6DSO"; static constexpr auto Type = SensorTypeID::LSM6DSO; - static constexpr float GyrFreq = 416; + static constexpr float GyrFreq = 208; static constexpr float AccFreq = 104; static constexpr float MagFreq = 120; static constexpr float TempFreq = 52; @@ -60,13 +60,7 @@ struct LSM6DSO : LSM6DSOutputHandler { static constexpr float TemperatureZROChange = 10.0; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 1.0f, - .biasClip = 2.0f, - .restThGyr = 1.0f, - .restThAcc = 0.192f, - }; + static constexpr VQFParams SensorVQFParams{}; struct Regs { struct WhoAmI { @@ -75,11 +69,11 @@ struct LSM6DSO : LSM6DSOutputHandler { }; struct Ctrl1XL { static constexpr uint8_t reg = 0x10; - static constexpr uint8_t value = (0b01001100); // XL at 104 Hz, 8g FS + static constexpr uint8_t value = (0b01001000); // XL at 104 Hz, 4g FS }; struct Ctrl2GY { static constexpr uint8_t reg = 0x11; - static constexpr uint8_t value = (0b01101000); // GY at 416 Hz, 1000dps FS + static constexpr uint8_t value = (0b01011000); // GY at 208 Hz, 1000dps FS }; struct Ctrl3C { static constexpr uint8_t reg = 0x12; @@ -90,7 +84,7 @@ struct LSM6DSO : LSM6DSOutputHandler { struct FifoCtrl3BDR { static constexpr uint8_t reg = 0x09; static constexpr uint8_t value - = (0b0110) | (0b0110 << 4); // gyro and accel batched at 417Hz + = 0b01010100; // Gyroscope batched into FIFO at 208Hz, Accel at 104Hz }; struct FifoCtrl4Mode { static constexpr uint8_t reg = 0x0a; diff --git a/src/sensors/softfusion/drivers/lsm6dsr.h b/src/sensors/softfusion/drivers/lsm6dsr.h index 7f1dd58..820c57f 100644 --- a/src/sensors/softfusion/drivers/lsm6dsr.h +++ b/src/sensors/softfusion/drivers/lsm6dsr.h @@ -31,16 +31,16 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { -// Driver uses acceleration range at 8g +// Driver uses acceleration range at 4g // and gyroscope range at 1000dps -// Gyroscope ODR = 416Hz, accel ODR = 104Hz +// Gyroscope ODR = 208Hz, accel ODR = 104Hz struct LSM6DSR : LSM6DSOutputHandler { static constexpr uint8_t Address = 0x6a; static constexpr auto Name = "LSM6DSR"; static constexpr auto Type = SensorTypeID::LSM6DSR; - static constexpr float GyrFreq = 416; + static constexpr float GyrFreq = 208; static constexpr float AccFreq = 104; static constexpr float MagFreq = 120; static constexpr float TempFreq = 52; @@ -58,13 +58,7 @@ struct LSM6DSR : LSM6DSOutputHandler { static constexpr float TemperatureZROChange = 20.0f; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 1.0f, - .biasClip = 2.0f, - .restThGyr = 1.0f, - .restThAcc = 0.192f, - }; + static constexpr VQFParams SensorVQFParams{}; struct Regs { struct WhoAmI { @@ -73,11 +67,11 @@ struct LSM6DSR : LSM6DSOutputHandler { }; struct Ctrl1XL { static constexpr uint8_t reg = 0x10; - static constexpr uint8_t value = (0b01001100); // XL at 104 Hz, 8g FS + static constexpr uint8_t value = (0b01001000); // XL at 104 Hz, 4g FS }; struct Ctrl2GY { static constexpr uint8_t reg = 0x11; - static constexpr uint8_t value = (0b01101000); // GY at 416 Hz, 1000dps FS + static constexpr uint8_t value = (0b01011000); // GY at 208 Hz, 1000dps FS }; struct Ctrl3C { static constexpr uint8_t reg = 0x12; @@ -88,7 +82,7 @@ struct LSM6DSR : LSM6DSOutputHandler { struct FifoCtrl3BDR { static constexpr uint8_t reg = 0x09; static constexpr uint8_t value - = (0b0110) | (0b0110 << 4); // gyro and accel batched at 417Hz + = 0b01010100; // Gyroscope batched into FIFO at 208Hz, Accel at 104Hz }; struct FifoCtrl4Mode { static constexpr uint8_t reg = 0x0a; diff --git a/src/sensors/softfusion/drivers/lsm6dsv.h b/src/sensors/softfusion/drivers/lsm6dsv.h index 5f1d1cf..ec16092 100644 --- a/src/sensors/softfusion/drivers/lsm6dsv.h +++ b/src/sensors/softfusion/drivers/lsm6dsv.h @@ -32,16 +32,16 @@ namespace SlimeVR::Sensors::SoftFusion::Drivers { -// Driver uses acceleration range at 8g +// Driver uses acceleration range at 4g // and gyroscope range at 1000dps -// Gyroscope ODR = 480Hz, accel ODR = 120Hz +// Gyroscope ODR = 240Hz, accel ODR = 120Hz struct LSM6DSV : LSM6DSOutputHandler { static constexpr uint8_t Address = 0x6a; static constexpr auto Name = "LSM6DSV"; static constexpr auto Type = SensorTypeID::LSM6DSV; - static constexpr float GyrFreq = 480; + static constexpr float GyrFreq = 240; static constexpr float AccFreq = 120; static constexpr float MagFreq = 120; static constexpr float TempFreq = 60; @@ -59,13 +59,7 @@ struct LSM6DSV : LSM6DSOutputHandler { static constexpr float TemperatureZROChange = 16.667f; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 1.0f, - .biasClip = 2.0f, - .restThGyr = 1.0f, - .restThAcc = 0.192f, - }; + static constexpr VQFParams SensorVQFParams{}; struct Regs { struct WhoAmI { @@ -82,7 +76,7 @@ struct LSM6DSV : LSM6DSOutputHandler { }; struct Ctrl2GODR { static constexpr uint8_t reg = 0x11; - static constexpr uint8_t value = (0b0011000); // 480Hz, HAODR + static constexpr uint8_t value = (0b0010111); // 240Hz, HAODR }; struct Ctrl3C { static constexpr uint8_t reg = 0x12; @@ -96,12 +90,12 @@ struct LSM6DSV : LSM6DSOutputHandler { }; struct Ctrl8XLFS { static constexpr uint8_t reg = 0x17; - static constexpr uint8_t value = (0b10); // 8g + static constexpr uint8_t value = (0b01); // 4g }; struct FifoCtrl3BDR { static constexpr uint8_t reg = 0x09; static constexpr uint8_t value - = (0b1000) | (0b1000 << 4); // gyro and accel batched at 480Hz + = 0b01110110; // Gyroscope batched into FIFO at 240Hz, Accel at 120Hz }; struct FifoCtrl4Mode { static constexpr uint8_t reg = 0x0a; diff --git a/src/sensors/softfusion/drivers/mpu6050.h b/src/sensors/softfusion/drivers/mpu6050.h index 9310158..3d544a0 100644 --- a/src/sensors/softfusion/drivers/mpu6050.h +++ b/src/sensors/softfusion/drivers/mpu6050.h @@ -70,13 +70,7 @@ struct MPU6050 { static constexpr float TemperatureZROChange = 1.6f; - static constexpr VQFParams SensorVQFParams{ - .motionBiasEstEnabled = true, - .biasSigmaInit = 20.0f, - .biasClip = 40.0f, - .restThGyr = 20.0f, - .restThAcc = 0.784f, - }; + static constexpr VQFParams SensorVQFParams{}; RegisterInterface& m_RegisterInterface; SlimeVR::Logging::Logger& m_Logger; diff --git a/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h b/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h index b6567e6..fe139b2 100644 --- a/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h +++ b/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h @@ -99,7 +99,10 @@ public: gyroBiasCalibrationStep.swapCalibrationIfNecessary(); - computeNextCalibrationStep(); + currentStep = &sampleRateCalibrationStep; + currentStep->start(); + nextCalibrationStep = CalibrationStepEnum::SAMPLING_RATE; + calculateZROChange(); printCalibration(); @@ -229,10 +232,7 @@ private: }; void computeNextCalibrationStep() { - if (!calibration.sensorTimestepsCalibrated) { - nextCalibrationStep = CalibrationStepEnum::SAMPLING_RATE; - currentStep = &sampleRateCalibrationStep; - } else if (!calibration.motionlessCalibrated && Base::HasMotionlessCalib) { + if (!calibration.motionlessCalibrated && Base::HasMotionlessCalib) { nextCalibrationStep = CalibrationStepEnum::MOTIONLESS; currentStep = &motionlessCalibrationStep; } else if (calibration.gyroPointsCalibrated == 0) { @@ -323,12 +323,12 @@ private: void printCalibration(CalibrationPrintFlags toPrint = PrintAllCalibration) { if (any(toPrint & CalibrationPrintFlags::TIMESTEPS)) { - if (calibration.sensorTimestepsCalibrated) { + if (activeCalibration.sensorTimestepsCalibrated) { logger.info( "Calibrated timesteps: Accel %f, Gyro %f, Temperature %f", - calibration.A_Ts, - calibration.G_Ts, - calibration.T_Ts + activeCalibration.A_Ts, + activeCalibration.G_Ts, + activeCalibration.T_Ts ); } else { logger.info("Sensor timesteps not calibrated"); @@ -389,12 +389,12 @@ private: } } - CalibrationStepEnum nextCalibrationStep = CalibrationStepEnum::MOTIONLESS; + CalibrationStepEnum nextCalibrationStep = CalibrationStepEnum::SAMPLING_RATE; static constexpr float initialStartupDelaySeconds = 5; uint64_t startupMillis = millis(); - SampleRateCalibrationStep sampleRateCalibrationStep{calibration}; + SampleRateCalibrationStep sampleRateCalibrationStep{activeCalibration}; MotionlessCalibrationStep motionlessCalibrationStep{ calibration, sensor From 106f00cf26783b5403d807a8e33e55998d4a7887 Mon Sep 17 00:00:00 2001 From: gorbit99 Date: Thu, 9 Oct 2025 11:02:45 +0200 Subject: [PATCH 09/25] WiFi Fix (#473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix some bugs with wifi rework * FormattingÅ * Surprised nothing stopped me from doing that but here we are * Move comments to their proper place * Thanks clang for autoimporting that, I'd appreciate if you didn't * Formatting --- src/network/wifihandler.cpp | 56 +++++++++++++++++++++++++++++-------- src/network/wifihandler.h | 3 ++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/network/wifihandler.cpp b/src/network/wifihandler.cpp index e9fb434..5a418e1 100644 --- a/src/network/wifihandler.cpp +++ b/src/network/wifihandler.cpp @@ -20,10 +20,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "network/wifihandler.h" + #include "GlobalVars.h" #include "globals.h" #if !ESP8266 #include "esp_wifi.h" +#include "esp_wifi_types.h" #endif namespace SlimeVR { @@ -50,7 +53,6 @@ bool WiFiNetwork::isConnected() const { void WiFiNetwork::setWiFiCredentials(const char* SSID, const char* pass) { wifiProvisioning.stopProvisioning(); - WiFi.persistent(true); tryConnecting(false, SSID, pass); retriedOnG = false; // Reset state, will get back into provisioning if can't connect @@ -61,15 +63,13 @@ void WiFiNetwork::setWiFiCredentials(const char* SSID, const char* pass) { IPAddress WiFiNetwork::getAddress() { return WiFi.localIP(); } void WiFiNetwork::setUp() { - // Don't need to save the already saved credentials or the hardcoded ones - WiFi.persistent(false); wifiHandlerLogger.info("Setting up WiFi"); WiFi.mode(WIFI_STA); WiFi.hostname("SlimeVR FBT Tracker"); wifiHandlerLogger.info( "Loaded credentials for SSID '%s' and pass length %d", - WiFi.SSID().c_str(), - WiFi.psk().length() + getSSID().c_str(), + getPassword().length() ); trySavedCredentials(); @@ -111,17 +111,40 @@ void WiFiNetwork::onConnected() { hadWifi = true; wifiHandlerLogger.info( "Connected successfully to SSID '%s', IP address %s", - WiFi.SSID().c_str(), + getSSID().c_str(), WiFi.localIP().toString().c_str() ); // Reset it, in case we just connected with server creds - WiFi.persistent(false); +} + +String WiFiNetwork::getSSID() { +#if ESP8266 + return WiFi.SSID(); +#else + // Necessary, because without a WiFi.begin(), ESP32 is not kind enough to load the + // SSID on its own, for whatever reason + wifi_config_t wifiConfig; + esp_wifi_get_config((wifi_interface_t)ESP_IF_WIFI_STA, &wifiConfig); + return {reinterpret_cast(wifiConfig.sta.ssid)}; +#endif +} + +String WiFiNetwork::getPassword() { +#if ESP8266 + return WiFi.psk(); +#else + // Same as above + wifi_config_t wifiConfig; + esp_wifi_get_config((wifi_interface_t)ESP_IF_WIFI_STA, &wifiConfig); + return {reinterpret_cast(wifiConfig.sta.password)}; +#endif } WiFiNetwork::WiFiReconnectionStatus WiFiNetwork::getWiFiState() { return wifiState; } void WiFiNetwork::upkeep() { wifiProvisioning.upkeepProvisioning(); + if (WiFi.status() == WL_CONNECTED) { if (!isConnected()) { onConnected(); @@ -143,7 +166,10 @@ void WiFiNetwork::upkeep() { return; } - reportWifiProgress(); + if (wifiState != WiFiReconnectionStatus::Failed) { + reportWifiProgress(); + } + if (millis() - wifiConnectionTimeout < static_cast(WiFiTimeoutSeconds * 1000) && WiFi.status() == WL_DISCONNECTED) { @@ -249,7 +275,7 @@ void WiFiNetwork::showConnectionAttemptFailed(const char* type) const { } bool WiFiNetwork::trySavedCredentials() { - if (WiFi.SSID().length() == 0) { + if (getSSID().length() == 0) { wifiHandlerLogger.debug("Skipping saved credentials attempt on 0-length SSID..." ); wifiState = WiFiReconnectionStatus::HardcodeAttempt; @@ -293,13 +319,21 @@ bool WiFiNetwork::tryHardcodedCredentials() { retriedOnG = true; wifiHandlerLogger.debug("Trying hardcoded credentials with PHY Mode G..."); - return tryConnecting(true, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); + // Don't need to save hardcoded credentials + WiFi.persistent(false); + auto result = tryConnecting(true, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); + WiFi.persistent(true); + return result; } retriedOnG = false; wifiState = WiFiReconnectionStatus::HardcodeAttempt; - return tryConnecting(false, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); + // Don't need to save hardcoded credentials + WiFi.persistent(false); + auto result = tryConnecting(false, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD); + WiFi.persistent(true); + return result; #else wifiState = WiFiReconnectionStatus::HardcodeAttempt; return false; diff --git a/src/network/wifihandler.h b/src/network/wifihandler.h index ac18479..7df6e85 100644 --- a/src/network/wifihandler.h +++ b/src/network/wifihandler.h @@ -63,6 +63,9 @@ private: void setStaticIPIfDefined(); void onConnected(); + static String getSSID(); + static String getPassword(); + bool trySavedCredentials(); bool tryHardcodedCredentials(); bool tryServerCredentials(); From f5c9648fbd6926d397d4b75bc46bebc62c80054a Mon Sep 17 00:00:00 2001 From: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> Date: Fri, 10 Oct 2025 08:49:25 +0200 Subject: [PATCH 10/25] Make sure wifi is set to persistent (#476) --- src/network/wifihandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/wifihandler.cpp b/src/network/wifihandler.cpp index 5a418e1..0f9a527 100644 --- a/src/network/wifihandler.cpp +++ b/src/network/wifihandler.cpp @@ -64,6 +64,7 @@ IPAddress WiFiNetwork::getAddress() { return WiFi.localIP(); } void WiFiNetwork::setUp() { wifiHandlerLogger.info("Setting up WiFi"); + WiFi.persistent(true); WiFi.mode(WIFI_STA); WiFi.hostname("SlimeVR FBT Tracker"); wifiHandlerLogger.info( From 02df66129cc7d82b46a99c8a47b6e650d2c78297 Mon Sep 17 00:00:00 2001 From: lucas lelievre Date: Wed, 22 Oct 2025 15:25:16 +0200 Subject: [PATCH 11/25] Nixos dev env (#477) * Basic Nix Flake for dev env * Remove udev rule command --- flake.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0d6154b --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1760878510, + "narHash": "sha256-K5Osef2qexezUfs0alLvZ7nQFTGS9DL2oTVsIXsqLgs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5e2a59a5b1a82f89f2c7e598302a9cacebb72a67", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..6998368 --- /dev/null +++ b/flake.nix @@ -0,0 +1,57 @@ +{ + description = "PlatformIO development environment"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + # PlatformIO + platformio + platformio-core + + # Python for PlatformIO + python3 + python3Packages.pip + python3Packages.virtualenv + + # Build tools + gcc + gnumake + cmake + + # Serial communication + picocom + minicom + + # USB access (for programming devices) + libusb1 + pkg-config + ]; + + shellHook = '' + # Set PlatformIO core directory to project-local directory + export PLATFORMIO_CORE_DIR=$PWD/.nix-platformio + + echo "PlatformIO development environment loaded" + echo "PlatformIO version: $(pio --version)" + echo "" + echo "Available commands:" + echo " pio init - Initialize a new PlatformIO project" + echo " pio run - Build the project" + echo " pio run -t upload - Upload to device" + echo " pio device monitor - Open serial monitor" + echo "" + ''; + }; + } + ); +} From 61ab745b387f1a145b57c7eae7c8653998e53822 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:11:34 +0300 Subject: [PATCH 12/25] Bump actions/upload-artifact from 4 to 5 (#479) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/actions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 64b90c9..5e255c9 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -58,7 +58,7 @@ jobs: run: python ./ci/build.py - name: Upload binaries - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: binaries path: ./build/*.bin From a17c1c2d3f44c3e3efce357249cce4825c8ce532 Mon Sep 17 00:00:00 2001 From: lucas lelievre Date: Sat, 1 Nov 2025 01:37:38 +0100 Subject: [PATCH 13/25] Refactor board defaults - Use a common system to handle user configurable defines (#474) * Tests * more changes * Remove the need for nodejs * Better preprocessor and ci * Fix ci maybe * Fix ci maybe * Fix ci maybe * Fix ci maybe + Add way to overide defaults from env vars * Temp fix for api tests * Small override fix * Fix override * More descriptions * More descriptions * Fix led + better typings * Better format * Bring back deleted files * Add all boards in platformio.ini * Always define Battery Pin and R1, R2 and Resistance * Checking Boards Default Config: BOARD_WEMOSD1MINI BOARD_NODEMCU BOARD_ESP01 BOARD_TTGO_TBASE * Format * Correcting Board Defaults: - BOARD_WROOM32 - BOARD_LOLIN_C3_MINI - BOARD_BEETLE32C3 - BOARD_ESP32C3DEVKITM1 - BOARD_ESP32C6DEVKITC1 - BOARD_WEMOSWROOM02 - BOARD_XIAO_ESP32C3 - BOARD_ESP32S3_SUPERMINI * Change IMU_AUTO to something else on boards that might crash with it. * remove IMU_UNKNOWN from selection * Preprocessor fixes * preprocessor defaults fixes + Make glove not use preprocessor --------- Co-authored-by: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> --- .gitignore | 3 + board-defaults.json | 589 +++++++++++++++++++++++++++++++++ board-defaults.schema.json | 242 ++++++++++++++ ci/build.py | 29 +- flake.nix | 25 +- platformio-tools.ini | 176 ---------- platformio.ini | 226 +++++++++---- scripts/preprocessor.py | 157 +++++++++ src/boards/boards_default.h | 172 +--------- src/boards/defines_helpers.cpp | 5 + 10 files changed, 1203 insertions(+), 421 deletions(-) create mode 100644 board-defaults.json create mode 100644 board-defaults.schema.json delete mode 100644 platformio-tools.ini create mode 100644 scripts/preprocessor.py diff --git a/.gitignore b/.gitignore index 047d442..5f22052 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ venv/ cache/ .idea/ compile_commands.json +node_modules/ +dist/ +.nix-platformio diff --git a/board-defaults.json b/board-defaults.json new file mode 100644 index 0000000..fb41e43 --- /dev/null +++ b/board-defaults.json @@ -0,0 +1,589 @@ +{ + "$schema": "board-defaults.schema.json", + "toolchain": "platformio", + "defaults": { + "BOARD_SLIMEVR": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_AUTO", + "int": "16", + "rotation": "DEG_270", + "scl": "12", + "sda": "14" + }, + { + "protocol": "I2C", + "imu": "IMU_AUTO", + "int": "13", + "rotation": "DEG_270", + "scl": "12", + "sda": "14" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 10, + "r2": 40.2, + "shieldR": 0, + "pin": "17" + }, + "LED": { + "LED_PIN": "2", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": true, + "needManualReboot": true, + "shouldOnlyUseDefaults": true + } + }, + "BOARD_SLIMEVR_V1_2": { + "values": { + "SENSORS": [ + { + "protocol": "SPI", + "imu": "IMU_AUTO", + "int": "2", + "rotation": "DEG_270" + }, + { + "protocol": "I2C", + "imu": "IMU_AUTO", + "int": "16", + "rotation": "DEG_270", + "scl": "5", + "sda": "4" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 10, + "r2": 40.2, + "shieldR": 0, + "pin": "17" + }, + "LED": { + "LED_PIN": "2", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": true, + "needManualReboot": true, + "shouldOnlyUseDefaults": true + } + }, + "BOARD_SLIMEVR_DEV": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_AUTO", + "int": "10", + "rotation": "DEG_270", + "scl": "5", + "sda": "4" + }, + { + "protocol": "I2C", + "imu": "IMU_AUTO", + "int": "13", + "rotation": "DEG_270", + "scl": "5", + "sda": "4" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 10, + "r2": 40.2, + "shieldR": 0, + "pin": "17" + }, + "LED": { + "LED_PIN": "2", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": true, + "needManualReboot": true, + "shouldOnlyUseDefaults": true + } + }, + "BOARD_WEMOSD1MINI": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_AUTO", + "int": "D5", + "rotation": "DEG_270", + "scl": "D1", + "sda": "D2" + }, + { + "protocol": "I2C", + "imu": "IMU_AUTO", + "int": "D6", + "rotation": "DEG_270", + "scl": "D1", + "sda": "D2" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "A0" + }, + "LED": { + "LED_PIN": "2", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_NODEMCU": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "D5", + "rotation": "DEG_270", + "scl": "D1", + "sda": "D2" + }, + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "D6", + "rotation": "DEG_270", + "scl": "D1", + "sda": "D2" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "A0" + }, + "LED": { + "LED_PIN": "2", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_ESP01": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "255", + "rotation": "DEG_270", + "scl": "0", + "sda": "2" + }, + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "255", + "rotation": "DEG_270", + "scl": "0", + "sda": "2" + } + ], + "BATTERY": { + "type": "BAT_INTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "255" + }, + "LED": { + "LED_PIN": "255", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_TTGO_TBASE": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "14", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + }, + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "13", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 0, + "pin": "A0" + }, + "LED": { + "LED_PIN": "2", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_WROOM32": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "23", + "rotation": "DEG_270", + "scl": "22", + "sda": "21" + }, + { + "protocol": "I2C", + "imu": "IMU_BNO085", + "int": "25", + "rotation": "DEG_270", + "scl": "22", + "sda": "21" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "36" + }, + "LED": { + "LED_PIN": "255", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_LOLIN_C3_MINI": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "6", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + }, + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "8", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "3" + }, + "LED": { + "LED_PIN": "7", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_BEETLE32C3": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "6", + "rotation": "DEG_270", + "scl": "9", + "sda": "8" + }, + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "7", + "rotation": "DEG_270", + "scl": "9", + "sda": "8" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "3" + }, + "LED": { + "LED_PIN": "10", + "LED_INVERTED": false + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_ESP32C3DEVKITM1": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "6", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + }, + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "7", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "3" + }, + "LED": { + "LED_PIN": "LED_BUILTIN", + "LED_INVERTED": false + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_ESP32C6DEVKITC1": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "6", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + }, + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "7", + "rotation": "DEG_270", + "scl": "4", + "sda": "5" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "3" + }, + "LED": { + "LED_PIN": "LED_BUILTIN", + "LED_INVERTED": false + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_WEMOSWROOM02": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "0", + "rotation": "DEG_270", + "scl": "14", + "sda": "2" + }, + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "4", + "rotation": "DEG_270", + "scl": "14", + "sda": "2" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 220, + "shieldR": 180, + "pin": "A0" + }, + "LED": { + "LED_PIN": "16", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_XIAO_ESP32C3": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "5", + "rotation": "DEG_270", + "scl": "7", + "sda": "6" + }, + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "10", + "rotation": "DEG_270", + "scl": "7", + "sda": "6" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 100, + "r2": 100, + "shieldR": 0, + "pin": "2" + }, + "LED": { + "LED_PIN": "4", + "LED_INVERTED": false + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + }, + "BOARD_ESP32S3_SUPERMINI": { + "values": { + "SENSORS": [ + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "5", + "rotation": "DEG_270", + "scl": "6", + "sda": "7" + }, + { + "protocol": "I2C", + "imu": "IMU_ICM45686", + "int": "4", + "rotation": "DEG_270", + "scl": "6", + "sda": "7" + } + ], + "BATTERY": { + "type": "BAT_EXTERNAL", + "r1": 10, + "r2": 40.2, + "shieldR": 0, + "pin": "A2" + }, + "LED": { + "LED_PIN": "LED_BUILTIN", + "LED_INVERTED": true + } + }, + "flashingRules": { + "applicationOffset": 0, + "needBootPress": false, + "needManualReboot": false, + "shouldOnlyUseDefaults": false + } + } + } +} diff --git a/board-defaults.schema.json b/board-defaults.schema.json new file mode 100644 index 0000000..227064f --- /dev/null +++ b/board-defaults.schema.json @@ -0,0 +1,242 @@ +{ + "$id": "board-defaults.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + + "$defs": { + "pin": { + "type": "string", + "pattern": "^[AD]?[0-9]\\d*$" + }, + + "LED_PIN": { + "type": "string", + "pattern": "^([AD]?[0-9]\\d*|LED_BUILTIN)$" + }, + + "IMU_TYPE": { + "type": "string", + "enum": [ + "IMU_AUTO", + "IMU_MPU9250", + "IMU_MPU6500", + "IMU_BNO080", + "IMU_BNO085", + "IMU_BNO055", + "IMU_MPU6050", + "IMU_BNO086", + "IMU_BMI160", + "IMU_ICM20948", + "IMU_ICM42688", + "IMU_LSM6DS3TRC", + "IMU_LSM6DSV", + "IMU_LSM6DSO", + "IMU_LSM6DSR", + "IMU_MPU6050_SF", + "IMU_ICM45686", + "IMU_ICM45605" + ], + "description": "Imu Type" + }, + + "PROTOCOL": { + "type": "string", + "enum": ["I2C", "SPI"], + "description": "Protocol" + }, + + "IMU_ROTATION": { + "type": "string", + "enum": ["DEG_0", "DEG_90", "DEG_180", "DEG_270"], + "description": "Protocol" + }, + + "I2C_IMU": { + "type": "object", + "description": "I2C Imu", + "properties": { + "protocol": { "const": "I2C" }, + "imu": { "$ref": "#/$defs/IMU_TYPE" }, + "sda": { "$ref": "#/$defs/pin", "description": "SDA Pin" }, + "scl": { "$ref": "#/$defs/pin", "description": "SCL Pin" }, + "int": { "$ref": "#/$defs/pin", "description": "INT Pin" }, + "address": { + "type": "number", + "description": "IMU Address" + }, + "rotation": { + "$ref": "#/$defs/IMU_ROTATION", + "description": "IMU Rotation" + } + }, + "required": ["protocol", "imu", "sda", "scl"] + }, + + "SPI_IMU": { + "type": "object", + "description": "SPI Imu", + "properties": { + "protocol": { "const": "SPI" }, + "imu": { "$ref": "#/$defs/IMU_TYPE" }, + "int": { "$ref": "#/$defs/pin", "description": "INT Pin" }, + "rotation": { + "$ref": "#/$defs/IMU_ROTATION", + "description": "IMU Rotation" + } + }, + "required": ["protocol", "imu"] + }, + + "IMU": { + "type": "object", + "discriminator": { + "propertyName": "protocol" + }, + "oneOf": [ + { "$ref": "#/$defs/I2C_IMU" }, + { "$ref": "#/$defs/SPI_IMU" } + ], + "required": ["protocol"] + }, + + "BOARD_TYPES": { + "type": "string", + "enum": [ + "BOARD_SLIMEVR", + "BOARD_SLIMEVR_V1_2", + "BOARD_SLIMEVR_DEV", + "BOARD_NODEMCU", + "BOARD_WEMOSD1MINI", + "BOARD_ESP01", + "BOARD_TTGO_TBASE", + "BOARD_WROOM32", + "BOARD_LOLIN_C3_MINI", + "BOARD_BEETLE32C3", + "BOARD_ESP32C3DEVKITM1", + "BOARD_ESP32C6DEVKITC1", + "BOARD_WEMOSWROOM02", + "BOARD_XIAO_ESP32C3", + "BOARD_ESP32S3_SUPERMINI" + ], + "description": "Board Type" + }, + + "BATTERY": { + "type": "object", + "discriminator": { + "propertyName": "type" + }, + "description": "Battery Settings", + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "BAT_EXTERNAL" }, + "shieldR": { + "type": "number", + "description": "Battery Shield Resistor (Ohms)" + }, + "r1": { "type": "number", "description": "R1 (Ohms)" }, + "r2": { "type": "number", "description": "R2 (Ohms)" }, + "pin": { + "$ref": "#/$defs/pin", + "description": "Battery Pin" + } + } + }, + { + "type": "object", + "properties": { + "type": { "const": "BAT_INTERNAL" } + } + }, + { + "type": "object", + "properties": { + "type": { "const": "BAT_MCP3021" } + } + }, + { + "type": "object", + "properties": { + "type": { "const": "BAT_INTERNAL_MCP3021" } + } + } + ], + "required": ["type"] + }, + + "BASIC_IMU_BOARD_CONFIG": { + "type": "object", + "properties": { + "SENSORS": { + "type": "array", + "items": { "$ref": "#/$defs/IMU" }, + "maxItems": 2, + "minItems": 1, + "description": "Sensors List" + }, + "LED": { + "type": "object", + "description": "Led Settings", + "properties": { + "LED_PIN": { + "$ref": "#/$defs/LED_PIN", + "description": "Led pin" + }, + "LED_INVERTED": { + "type": "boolean", + "description": "Led inverted" + } + }, + "required": ["LED_PIN", "LED_INVERTED"] + }, + "BATTERY": { "$ref": "#/$defs/BATTERY" } + }, + "required": ["SENSORS", "LED", "BATTERY"] + }, + + "BoardConfig": { + "type": "object", + "properties": { + "values": { + "$ref": "#/$defs/BASIC_IMU_BOARD_CONFIG" + }, + "flashingRules": { + "type": "object", + "properties": { + "needBootPress": { "type": "boolean" }, + "needManualReboot": { "type": "boolean" }, + "shouldOnlyUseDefaults": { "type": "boolean" }, + "applicationOffset": { "type": "integer" } + }, + "required": [ + "needBootPress", + "needManualReboot", + "shouldOnlyUseDefaults", + "applicationOffset" + ] + }, + "ignored": { + "type": "boolean" + } + }, + "required": ["values", "flashingRules"] + } + }, + + "type": "object", + "properties": { + "toolchain": { + "type": "string", + "enum": ["platformio"] + }, + "defaults": { + "type": "object", + "propertyNames": { + "$ref": "#/$defs/BOARD_TYPES" + }, + "additionalProperties": { "$ref": "#/$defs/BoardConfig" } + } + }, + "required": ["toolchain", "defaults"] +} diff --git a/ci/build.py b/ci/build.py index 75df6a3..9a64406 100644 --- a/ci/build.py +++ b/ci/build.py @@ -32,12 +32,13 @@ def get_matrix() -> List[DeviceConfiguration]: matrix: List[DeviceConfiguration] = [] config = configparser.ConfigParser() - config.read("./platformio-tools.ini") + config.read("./platformio.ini") for section in config.sections(): - if section == "env": + split = section.split(":") + if len(split) != 2 or split[0] != 'env': continue - board = section.split(":")[1] + board = split[1] platform = config[section]["platform"] platformio_board = config[section]["board"] @@ -51,36 +52,15 @@ def get_matrix() -> List[DeviceConfiguration]: def prepare() -> None: print(f"🡢 {COLOR_CYAN}Preparation{COLOR_RESET}") - - print(f" 🡢 {COLOR_GRAY}Backing up platformio.ini{COLOR_RESET}") - shutil.copy("./platformio.ini", "platformio.ini.bak") - - print( - f" 🡢 {COLOR_GRAY}Switching platformio.ini to platformio-tools.ini{COLOR_RESET}") - shutil.copy("./platformio-tools.ini", "platformio.ini") - if os.path.exists("./build"): print(f" 🡢 {COLOR_GRAY}Removing existing build folder...{COLOR_RESET}") shutil.rmtree("./build") - print(f" 🡢 {COLOR_GRAY}Creating build folder...{COLOR_RESET}") os.mkdir("./build") print(f" 🡢 {COLOR_GREEN}Success!{COLOR_RESET}") -def cleanup() -> None: - print(f"🡢 {COLOR_CYAN}Cleanup{COLOR_RESET}") - - print(f" 🡢 {COLOR_GRAY}Restoring platformio.ini...{COLOR_RESET}") - shutil.copy("platformio.ini.bak", "platformio.ini") - - print(f" 🡢 {COLOR_GRAY}Removing platformio.ini.bak...{COLOR_RESET}") - os.remove("platformio.ini.bak") - - print(f" 🡢 {COLOR_GREEN}Success!{COLOR_RESET}") - - def build() -> int: print(f"🡢 {COLOR_CYAN}Build{COLOR_RESET}") @@ -135,7 +115,6 @@ def build_for_device(device: DeviceConfiguration) -> bool: def main() -> None: prepare() code = build() - cleanup() sys.exit(code) diff --git a/flake.nix b/flake.nix index 6998368..ed7b033 100644 --- a/flake.nix +++ b/flake.nix @@ -18,11 +18,21 @@ platformio platformio-core - # Python for PlatformIO + # Python for PlatformIO with needed packages python3 python3Packages.pip python3Packages.virtualenv + # Pre-install Python packages that need compilation + python3Packages.jsonschema + python3Packages.rpds-py + python3Packages.attrs + python3Packages.referencing + + # Rust toolchain (in case compilation is needed) + rustc + cargo + # Build tools gcc gnumake @@ -41,14 +51,27 @@ # Set PlatformIO core directory to project-local directory export PLATFORMIO_CORE_DIR=$PWD/.nix-platformio + # Create and activate Python virtual environment + if [ ! -d .venv ]; then + echo "Creating Python virtual environment..." + python3 -m venv .venv --system-site-packages + fi + source .venv/bin/activate + + # Prefer binary wheels over building from source + export PIP_PREFER_BINARY=1 + echo "PlatformIO development environment loaded" + echo "Python virtual environment activated: .venv" echo "PlatformIO version: $(pio --version)" + echo "Python version: $(python --version)" echo "" echo "Available commands:" echo " pio init - Initialize a new PlatformIO project" echo " pio run - Build the project" echo " pio run -t upload - Upload to device" echo " pio device monitor - Open serial monitor" + echo " pip install - Install Python packages in venv" echo "" ''; }; diff --git a/platformio-tools.ini b/platformio-tools.ini deleted file mode 100644 index ba718e1..0000000 --- a/platformio-tools.ini +++ /dev/null @@ -1,176 +0,0 @@ -[env] -lib_deps= - https://github.com/SlimeVR/CmdParser.git - https://github.com/SlimeVR/base64_arduino.git - https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library.git - https://github.com/hideakitai/PCA9547.git -monitor_speed = 115200 -framework = arduino -build_flags = - !python scripts/get_git_commit.py - -O2 - -std=gnu++2a -build_unflags = - -Os - -std=gnu++11 -std=gnu++17 - -[env:BOARD_SLIMEVR] -platform = espressif8266 @ 4.2.1 -board = esp12e -build_flags = - ${env.build_flags} - -D BOARD=BOARD_SLIMEVR - -D VENDOR_NAME='"SlimeVR"' - -D VENDOR_URL='"https://slimevr.dev"' - -D PRODUCT_NAME='"SlimeVR Tracker"' - -D UPDATE_ADDRESS='"SlimeVR/SlimeVR-Tracker-ESP"' - -D UPDATE_NAME='"BOARD_SLIMEVR-firmware"' - -[env:BOARD_SLIMEVR_V1_2] -platform = espressif8266 @ 4.2.1 -board = esp12e -build_flags = - ${env.build_flags} - -D BOARD=BOARD_SLIMEVR_V1_2 - -D VENDOR_NAME='"SlimeVR"' - -D VENDOR_URL='"https://slimevr.dev"' - -D PRODUCT_NAME='"SlimeVR Tracker v1.2"' - -D UPDATE_ADDRESS='"SlimeVR/SlimeVR-Tracker-ESP"' - -D UPDATE_NAME='"BOARD_SLIMEVR_V1_2-firmware"' - -[env:BOARD_SLIMEVR_DEV] -platform = espressif8266 @ 4.2.1 -board = esp12e -build_flags = - ${env.build_flags} - -D BOARD=BOARD_SLIMEVR_DEV - -D VENDOR_NAME='"SlimeVR"' - -D PRODUCT_NAME='"SlimeVR Tracker (dev)"' - -[env:BOARD_GLOVE_IMU_SLIMEVR_DEV] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -build_flags = - ${env.build_flags} - -DESP32C3 - -D BOARD=BOARD_GLOVE_IMU_SLIMEVR_DEV - -D PRODUCT_NAME='"SlimeVR Glove (dev)"' -board = lolin_c3_mini - -[env:BOARD_NODEMCU] -platform = espressif8266 @ 4.2.1 -board = esp12e -build_flags = - ${env.build_flags} - -D BOARD=BOARD_NODEMCU - -[env:BOARD_WEMOSD1MINI] -platform = espressif8266 @ 4.2.1 -board = esp12e -build_flags = - ${env.build_flags} - -D BOARD=BOARD_WEMOSD1MINI - -[env:BOARD_TTGO_TBASE] -platform = espressif8266 @ 4.2.1 -board = esp12e -build_flags = - ${env.build_flags} - -D BOARD=BOARD_TTGO_TBASE - -[env:BOARD_WEMOSWROOM02] -platform = espressif8266 @ 4.2.1 -board = esp12e -build_flags = - ${env.build_flags} - -D BOARD=BOARD_NODEMCU - -[env:BOARD_WROOM32] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -board = esp32dev -build_flags = - ${env.build_flags} - -D BOARD=BOARD_WROOM32 - -[env:BOARD_ESP01] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -board = esp32dev -build_flags = - ${env.build_flags} - -D BOARD=BOARD_ESP01 - -[env:BOARD_LOLIN_C3_MINI] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -build_flags = - ${env.build_flags} - -DESP32C3 - -D BOARD=BOARD_LOLIN_C3_MINI -board = lolin_c3_mini - -[env:BOARD_BEETLE32C3] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -build_flags = - ${env.build_flags} - -DESP32C3 - -D BOARD=BOARD_BEETLE32C3 -board = dfrobot_beetle_esp32c3 - -[env:BOARD_ESP32C3DEVKITM1] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -build_flags = - ${env.build_flags} - -DESP32C3 - -D BOARD=BOARD_ESP32C3DEVKITM1 -board = esp32-c3-devkitm-1 - -[env:BOARD_ESP32C6DEVKITC1] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip -build_flags = - ${env.build_flags} - -DESP32C6 - -D BOARD=BOARD_ESP32C6DEVKITC1 -board = esp32-c6-devkitc-1 - -[env:BOARD_XIAO_ESP32C3] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -build_flags = - ${env.build_flags} - -DESP32C3 - -D BOARD=BOARD_XIAO_ESP32C3 -board = seeed_xiao_esp32c3 - -[env:BOARD_ESP32S3_SUPERMINI] -platform = espressif32 @ 6.7.0 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -build_flags = - ${env.build_flags} - -DARDUINO_USB_MODE=1 - -DESP32S3 - -D BOARD=BOARD_ESP32S3_SUPERMINI -board = esp32s3_supermini -board_upload.use_1200bps_touch = 1 -board_upload.wait_for_upload_port = 1 -board_upload.require_upload_port = 1 -upload_speed = 921600 diff --git a/platformio.ini b/platformio.ini index fd7dbd0..a0dc9ff 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,7 @@ [platformio] build_cache_dir = cache +default_envs = BOARD_WEMOSD1MINI [env] lib_deps= @@ -26,7 +27,8 @@ monitor_filters = colorize ;monitor_rts = 0 ;monitor_dtr = 0 framework = arduino -build_flags = +extra_scripts = pre:scripts/preprocessor.py +build_flags = !python scripts/get_git_commit.py ;If you want to set hardcoded WiFi SSID and password, uncomment and edit the lines below ;To uncomment, only remove ";" and leave the two spaces in front of the tags @@ -60,39 +62,57 @@ build_unflags = -Os -std=gnu++11 -std=gnu++17 ;upload_flags = ; --auth=SlimeVR-OTA -; Settings for different boards - -[env:esp12e] -platform = espressif8266 @ 4.2.1 -board = esp12e -; 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 -upload_speed = 921600 - ; Uncomment below if you want to build for ESP-01 ;[env:esp01_1m] ;platform = espressif8266 @ 4.2.1 ;board = esp01_1m ;board_build.arduino.ldscript = "eagle.flash.1m64.ld" -; Uncomment below if you want to build for ESP8285 (ESP8266 with embedded Flash) -;[env:esp8285] -;platform = espressif8266 @ 4.2.1 -;board = esp8285 -;board_build.arduino.ldscript = "eagle.flash.1m64.ld" -;board_build.flash_mode = dout +[env:BOARD_WEMOSD1MINI] +platform = espressif8266 @ 4.2.1 +board = esp12e +custom_slime_board = BOARD_WEMOSD1MINI -; 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] -; platform = espressif32 @ 6.7.0 -; platform_packages = -; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 -; framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -; 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 -;upload_speed = 921600 +[env:BOARD_NODEMCU] +platform = espressif8266 @ 4.2.1 +board = esp12e +custom_slime_board = BOARD_NODEMCU +[env:BOARD_TTGO_TBASE] +platform = espressif8266 @ 4.2.1 +board = esp12e +custom_slime_board = BOARD_TTGO_TBASE + +[env:BOARD_WEMOSWROOM02] +platform = espressif8266 @ 4.2.1 +board = esp12e +custom_slime_board = BOARD_WEMOSWROOM02 + +[env:BOARD_WROOM32] +platform = espressif32 @ 6.7.0 +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip +board = esp32dev +custom_slime_board = BOARD_WROOM32 + +[env:BOARD_ESP01] +platform = espressif8266 @ 4.2.1 +board = esp01_1m +board_build.arduino.ldscript = "eagle.flash.1m64.ld" +custom_slime_board = BOARD_ESP01 + +[env:BOARD_LOLIN_C3_MINI] +platform = espressif32 @ 6.7.0 +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip +custom_slime_board = BOARD_LOLIN_C3_MINI +build_flags = + ${env.build_flags} + -DESP32C3 +board = lolin_c3_mini +monitor_filters = colorize, esp32_exception_decoder ; If you want to use a ESP32C3, you can use this (experimental) ; Check your board name at https://docs.platformio.org/en/latest/platforms/espressif32.html#boards ; There are 2 main Boardtypes: @@ -102,38 +122,126 @@ upload_speed = 921600 ; -DARDUINO_USB_MODE=1 ; -DARDUINO_USB_CDC_ON_BOOT=1 -;[env:esp32c3] -;platform = espressif32 @ 6.7.0 -;platform_packages = -; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 -; framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -;build_flags = -; ${env.build_flags} -; -DESP32C3 -;board = lolin_c3_mini -;monitor_filters = colorize, esp32_exception_decoder +[env:BOARD_BEETLE32C3] +platform = espressif32 @ 6.7.0 +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip +custom_slime_board = BOARD_BEETLE32C3 +build_flags = + ${env.build_flags} + -DESP32C3 +board = dfrobot_beetle_esp32c3 +monitor_filters = colorize, esp32_exception_decoder +; If you want to use a ESP32C3, you can use this (experimental) +; Check your board name at https://docs.platformio.org/en/latest/platforms/espressif32.html#boards +; There are 2 main Boardtypes: +; 1. Boards that use a USB 2 Serial Chipset ( esp32-c3-devkitm-1, ttgo-t-oi-plus ) +; 2. Boards that relay on the USB interface of the ESP32C3 ( lolin_c3_mini , dfrobot_beetle_esp32c3) +; On this board there are 2 type some of them need to have set the build flag (menuconfig) +; -DARDUINO_USB_MODE=1 +; -DARDUINO_USB_CDC_ON_BOOT=1 -; If you want to use a ESP32C6, you can use this (experimental) -;[env:esp32c6] -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip -;board = esp32-c6-devkitc-1 -;build_flags = -; ${env.build_flags} -; -DESP32C6 -; -DARDUINO_USB_MODE=1 -; -DARDUINO_USB_CDC_ON_BOOT=1 +[env:BOARD_ESP32C3DEVKITM1] +platform = espressif32 @ 6.7.0 +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip +custom_slime_board = BOARD_ESP32C3DEVKITM1 +build_flags = + ${env.build_flags} + -DESP32C3 +board = esp32-c3-devkitm-1 +monitor_filters = colorize, esp32_exception_decoder +; If you want to use a ESP32C3, you can use this (experimental) +; Check your board name at https://docs.platformio.org/en/latest/platforms/espressif32.html#boards +; There are 2 main Boardtypes: +; 1. Boards that use a USB 2 Serial Chipset ( esp32-c3-devkitm-1, ttgo-t-oi-plus ) +; 2. Boards that relay on the USB interface of the ESP32C3 ( lolin_c3_mini , dfrobot_beetle_esp32c3) +; On this board there are 2 type some of them need to have set the build flag (menuconfig) +; -DARDUINO_USB_MODE=1 +; -DARDUINO_USB_CDC_ON_BOOT=1 -;[env:BOARD_ESP32S3_SUPERMINI] -;platform = espressif32 @ 6.7.0 -;platform_packages = -; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 -; framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip -;build_flags = -; ${env.build_flags} -; -DARDUINO_USB_MODE=1 -; -DESP32S3 -;board = esp32s3_supermini -;board_upload.use_1200bps_touch = 1 -;board_upload.wait_for_upload_port = 1 -;board_upload.require_upload_port = 1 -;upload_speed = 921600 +[env:BOARD_ESP32C6DEVKITC1] +platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip +custom_slime_board = BOARD_ESP32C6DEVKITC1 +build_flags = + ${env.build_flags} + -DESP32C6 + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 +board = esp32-c6-devkitc-1 + +[env:BOARD_XIAO_ESP32C3] +platform = espressif32 @ 6.7.0 +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip +custom_slime_board = BOARD_XIAO_ESP32C3 +build_flags = + ${env.build_flags} + -DESP32C3 +board = seeed_xiao_esp32c3 +monitor_filters = colorize, esp32_exception_decoder + +[env:BOARD_ESP32S3_SUPERMINI] +platform = espressif32 @ 6.7.0 +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip +custom_slime_board = BOARD_ESP32S3_SUPERMINI +build_flags = + ${env.build_flags} + -DARDUINO_USB_MODE=1 + -DESP32S3 +board = esp32s3_supermini +board_upload.use_1200bps_touch = 1 +board_upload.wait_for_upload_port = 1 +board_upload.require_upload_port = 1 +upload_speed = 921600 + +[env:BOARD_SLIMEVR] +platform = espressif8266 @ 4.2.1 +board = esp12e +custom_slime_board = BOARD_SLIMEVR +build_flags = + ${env.build_flags} + -D VENDOR_NAME='"SlimeVR"' + -D VENDOR_URL='"https://slimevr.dev"' + -D PRODUCT_NAME='"SlimeVR Tracker"' + -D UPDATE_ADDRESS='"SlimeVR/SlimeVR-Tracker-ESP"' + -D UPDATE_NAME='"BOARD_SLIMEVR-firmware"' + +[env:BOARD_SLIMEVR_V1_2] +platform = espressif8266 @ 4.2.1 +board = esp12e +custom_slime_board = BOARD_SLIMEVR_V1_2 +build_flags = + ${env.build_flags} + -D VENDOR_NAME='"SlimeVR"' + -D VENDOR_URL='"https://slimevr.dev"' + -D PRODUCT_NAME='"SlimeVR Tracker v1.2"' + -D UPDATE_ADDRESS='"SlimeVR/SlimeVR-Tracker-ESP"' + -D UPDATE_NAME='"BOARD_SLIMEVR_V1_2-firmware"' + +[env:BOARD_SLIMEVR_DEV] +platform = espressif8266 @ 4.2.1 +board = esp12e +custom_slime_board = BOARD_SLIMEVR_DEV +build_flags = + ${env.build_flags} + -D VENDOR_NAME='"SlimeVR"' + -D PRODUCT_NAME='"SlimeVR Tracker (dev)"' + +[env:BOARD_GLOVE_IMU_SLIMEVR_DEV] +platform = espressif32 @ 6.7.0 +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip +build_flags = + ${env.build_flags} + -D BOARD=BOARD_GLOVE_IMU_SLIMEVR_DEV + -DESP32C3 + -D PRODUCT_NAME='"SlimeVR Glove (dev)"' +board = lolin_c3_mini +monitor_filters = colorize, esp32_exception_decoder diff --git a/scripts/preprocessor.py b/scripts/preprocessor.py new file mode 100644 index 0000000..8692deb --- /dev/null +++ b/scripts/preprocessor.py @@ -0,0 +1,157 @@ +import json +import re +import os +from pathlib import Path +from typing import Union, Optional, Dict, Any, List + +Import("env") + +try: + import jsonschema +except: + env.Execute( + env.VerboseAction( + '$PYTHONEXE -m pip install "jsonschema==4.22.0"', + "Installing jsonschema for validation", + ) + ) + +from jsonschema import Draft202012Validator, exceptions as jsonschema_exceptions + +def _load_json(maybe_path_or_dict: Union[str, Path, dict]) -> dict: + """Load JSON file or accept dict directly.""" + if isinstance(maybe_path_or_dict, dict): + return maybe_path_or_dict + p = Path(maybe_path_or_dict) + if not p.exists(): + raise FileNotFoundError(f"File not found: {p}") + try: + return json.loads(p.read_text(encoding="utf-8")) + except json.JSONDecodeError as e: + raise ValueError(f"Invalid JSON file {p}: {e}") + + +def _format_raw_value(value: Any) -> str: + """Format booleans for c/cpp, otherwise str(value).""" + if isinstance(value, bool): + return "true" if value else "false" + return str(value) + + +def _build_board_flags(defaults: dict, board_name: str) -> List[str]: + """Construct list of -D flags for one board.""" + if "defaults" not in defaults: + raise ValueError("Missing top-level 'defaults' key in defaults JSON.") + if board_name not in defaults["defaults"]: + raise ValueError(f"Invalid board selected - {board_name}") + + board_defaults = defaults["defaults"][board_name] + values = board_defaults.get("values", {}) + + args: Dict[str, Dict[str, Any]] = {} + + def add(key: str, value: Any, value_type: str): + if value is not None: + args[key] = {"value": value, "type": value_type} + + add("BOARD", board_name, "raw") + add("LED_PIN", values.get("LED").get("LED_PIN"), "pin") + add("LED_INVERTED", values.get("LED").get("LED_INVERTED"), "raw") + + sensors = values.get("SENSORS") + if sensors: + for index, sensor in enumerate(sensors): + if index == 0: + add("IMU", sensor.get("imu"), "raw") + add("PIN_IMU_INT", sensor.get("int"), "pin") + add("IMU_ROTATION", sensor.get("rotation"), "raw") + if sensor.get("protocol") == "I2C": + add("PRIMARY_IMU_ADDRESS_ONE", sensor.get("address"), "number") + if index == 1: + add("SECOND_IMU", sensor.get("imu"), "raw") + add("PIN_IMU_INT_2", sensor.get("int"), "pin") + add("SECOND_IMU_ROTATION", sensor.get("rotation"), "raw") + if sensor.get("protocol") == "I2C": + add("SECONDARY_IMU_ADDRESS_TWO", sensor.get("address"), "number") + + if sensor.get("protocol") == "I2C": + add("PIN_IMU_SDA", sensor.get("sda"), "pin") + add("PIN_IMU_SCL", sensor.get("scl"), "pin") + + battery = values.get("BATTERY") + if battery: + add("BATTERY_MONITOR", battery.get("type"), "raw") + add("PIN_BATTERY_LEVEL", battery.get("pin", 255), "pin") + add("BATTERY_SHIELD_RESISTANCE", battery.get("shieldR", 180), "number") + add("BATTERY_SHIELD_R1", battery.get("r1", 100), "number") + add("BATTERY_SHIELD_R2", battery.get("r2", 220), "number") + + parts: List[str] = [] + for key, meta in args.items(): + val = meta["value"] + typ = meta["type"] + + if typ == "pin": + if isinstance(val, str) and re.search(r"[AD]", val): + parts.append(f"-D{key}='{val}'") + else: + parts.append(f"-D{key}={_format_raw_value(val)}") + elif typ == "string": + parts.append(f"-D{key}='{val}'") + elif typ in ("raw", "number"): + parts.append(f"-D{key}={_format_raw_value(val)}") + + return parts + + +def build_boards( + schema_obj, + defaults_obj, + board_name: Optional[str] = None, +) -> Dict[str, List[str]]: + """ + Validate defaults.json against board-defaults.schema.json using jsonschema, + and return { board_name: [list of -D flags] }. + """ + validator = Draft202012Validator(schema_obj) + errors = sorted(validator.iter_errors(defaults_obj), key=lambda e: e.path) + + if errors: + print("✖ JSON Schema validation failed:") + for err in errors: + path = "/".join(map(str, err.path)) or "(root)" + print(f" • Path: {path}") + print(f" Error: {err.message}") + if err.context: + for ctx in err.context: + print(f" ↳ {ctx.message}") + raise ValueError(f"{len(errors)} schema validation errors found.") + + out: Dict[str, List[str]] = {} + if board_name: + out[board_name] = _build_board_flags(defaults_obj, board_name) + else: + for name in defaults_obj.get("defaults", {}).keys(): + out[name] = _build_board_flags(defaults_obj, name) + + return out + +schema_obj = _load_json("./board-defaults.schema.json") +defaults_obj = _load_json("./board-defaults.json") +slime_board = env.GetProjectOption("custom_slime_board", None) +if slime_board: + if 'SLIMEVR_OVERRIDE_DEFAULTS' in os.environ and slime_board in defaults_obj['defaults']: + print(">>> OVERIDING BOARD DEFAULTS ", os.environ['SLIMEVR_OVERRIDE_DEFAULTS']) + defaults_obj['defaults'][slime_board]['values'] = json.loads(os.environ['SLIMEVR_OVERRIDE_DEFAULTS']) + + output_flags = build_boards( + schema_obj, + defaults_obj, + slime_board, + ) + output_flags = output_flags.get(slime_board, []) if isinstance(output_flags, dict) else [] + + print(">>> Appending build flags:", output_flags) + env.Append(BUILD_FLAGS=output_flags) +else: + print(">>> custom_slime_board not set - skipping") diff --git a/src/boards/boards_default.h b/src/boards/boards_default.h index f329728..b16bd73 100644 --- a/src/boards/boards_default.h +++ b/src/boards/boards_default.h @@ -27,166 +27,6 @@ #include "defines_helpers.h" -// Board-specific configurations -#if BOARD == BOARD_SLIMEVR - -SDA(14) -SCL(12) -INT(16) -INT2(13) -BATTERY(17) -LED(2) -INVERTED_LED(true) -BATTERY_SHIELD_R(0) -BATTERY_R1(10) -BATTERY_R2(40.2) - -#elif BOARD == BOARD_SLIMEVR_V1_2 - -SDA(4) -SCL(5) -INT(2) -INT2(16) -BATTERY(17) -LED(2) -INVERTED_LED(true) -BATTERY_SHIELD_R(0) -BATTERY_R1(10) -BATTERY_R2(40.2) - -#elif BOARD == BOARD_SLIMEVR_LEGACY || BOARD == BOARD_SLIMEVR_DEV - -SDA(4) -SCL(5) -INT(10) -INT2(13) -BATTERY(17) -LED(2) -INVERTED_LED(true) -BATTERY_SHIELD_R(0) -BATTERY_R1(10) -BATTERY_R2(40.2) - -#elif BOARD == BOARD_NODEMCU || BOARD == BOARD_WEMOSD1MINI - -SDA(D2) -SCL(D1) -INT(D5) -INT2(D6) -BATTERY(A0) -BATTERY_SHIELD_R(180) -BATTERY_R1(100) -BATTERY_R2(220) - -#elif BOARD == BOARD_ESP01 - -SDA(2) -SCL(0) -INT(255) -INT2(255) -BATTERY(255) -LED(LED_OFF) -INVERTED_LED(false) - -#elif BOARD == BOARD_TTGO_TBASE - -SDA(5) -SCL(4) -INT(14) -INT2(13) -BATTERY(A0) - -#elif BOARD == BOARD_CUSTOM - -// Define pins by the examples above - -#elif BOARD == BOARD_WROOM32 - -SDA(21) -SCL(22) -INT(23) -INT2(25) -BATTERY(36) - -#elif BOARD == BOARD_LOLIN_C3_MINI - -SDA(5) -SCL(4) -INT(6) -INT2(8) -BATTERY(3) -LED(7) - -#elif BOARD == BOARD_BEETLE32C3 - -SDA(8) -SCL(9) -INT(6) -INT2(7) -BATTERY(3) -LED(10) -INVERTED_LED(false) - -#elif BOARD == BOARD_ESP32C3DEVKITM1 || BOARD == BOARD_ESP32C6DEVKITC1 - -SDA(5) -SCL(4) -INT(6) -INT2(7) -BATTERY(3) -LED(LED_OFF) - -#elif BOARD == BOARD_WEMOSWROOM02 - -SDA(2) -SCL(14) -INT(0) -INT2(4) -BATTERY(A0) -LED(16) -INVERTED_LED(true) - -#elif BOARD == BOARD_XIAO_ESP32C3 - -SDA(6) -SCL(7) // D5 -INT(5) // D3 -INT2(10) // D10 -LED(4) // D2 -INVERTED_LED(false) -BATTERY(2) // D0 / A0 -BATTERY_SHIELD_R(0) -BATTERY_R1(100) -BATTERY_R2(100) - -#elif BOARD == BOARD_GLOVE_IMU_SLIMEVR_DEV - -SDA(1) -SCL(0) -#define PCA_ADDR 0x70 -INT(16) -INT2(13) -BATTERY(3) -LED(2) -INVERTED_LED(true) -BATTERY_SHIELD_R(0) -BATTERY_R1(10) -BATTERY_R2(40.2) - -#elif BOARD == BOARD_ESP32S3_SUPERMINI - -SDA(7) -SCL(6) -INT(5) -INT2(4) -BATTERY(A2) // IO3 -BATTERY_SHIELD_R(0) -BATTERY_R1(10) -BATTERY_R2(40.2) -LED(LED_BUILTIN) - -#endif - // Default IMU pinouts and definitions for default tracker types #if BOARD != BOARD_GLOVE_IMU_SLIMEVR_DEV @@ -253,6 +93,18 @@ PIN_IMU_SDA, PRIMARY_IMU_OPTIONAL, BMI160_QMC_REMAP) \ #endif #else // BOARD == BOARD_GLOVE_IMU_SLIMEVR_DEV +SDA(1) +SCL(0) +#define PCA_ADDR 0x70 +INT(16) +INT2(13) +BATTERY(3) +LED(2) +INVERTED_LED(true) +BATTERY_SHIELD_R(0) +BATTERY_R1(10) +BATTERY_R2(40.2) + #include "glove_default.h" #endif // BOARD != BOARD_GLOVE_IMU_SLIMEVR_DEV diff --git a/src/boards/defines_helpers.cpp b/src/boards/defines_helpers.cpp index a2c5ad7..34272bf 100644 --- a/src/boards/defines_helpers.cpp +++ b/src/boards/defines_helpers.cpp @@ -29,5 +29,10 @@ #define LED_BUILTIN LED_OFF #endif +#ifndef LED_PIN extern const uint8_t __attribute__((weak)) LED_PIN = LED_BUILTIN; +#endif + +#ifndef LED_INVERTED extern const bool __attribute__((weak)) LED_INVERTED = true; +#endif From 91b6318a8a4aab3314e43436ec677c39ce7743ed Mon Sep 17 00:00:00 2001 From: lucas lelievre Date: Tue, 4 Nov 2025 01:25:46 +0100 Subject: [PATCH 14/25] Make git_commit script use firmware version env for fw tool (#482) Make git_commit script use firmware verion env for fw tool when built from source --- scripts/get_git_commit.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/get_git_commit.py b/scripts/get_git_commit.py index d780736..132bcd8 100644 --- a/scripts/get_git_commit.py +++ b/scripts/get_git_commit.py @@ -36,7 +36,10 @@ except Exception: output = f"-DGIT_REV='\"{revision}\"'" -if tag != "": +fwVersion = os.environ.get("FIRMWARE_VERSION") +if fwVersion is not None and fwVersion != "": + output += f" -DFIRMWARE_VERSION='\"{fwVersion}\"'" +elif tag != "": output += f" -DFIRMWARE_VERSION='\"{tag}\"'" elif branch != "": output += f" -DFIRMWARE_VERSION='\"{branch}\"'" From 003128c3b6276927106559f548fc918029f21705 Mon Sep 17 00:00:00 2001 From: lucas lelievre Date: Tue, 4 Nov 2025 01:26:52 +0100 Subject: [PATCH 15/25] Add full support for sensor list in preprocessor (#483) * Add support for sensor list in preprocessor * Fix quotes --- board-defaults.json | 1 + board-defaults.schema.json | 6 +- scripts/preprocessor.py | 122 ++++++++++++++++++++++--------------- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/board-defaults.json b/board-defaults.json index fb41e43..fba0b1c 100644 --- a/board-defaults.json +++ b/board-defaults.json @@ -48,6 +48,7 @@ "protocol": "SPI", "imu": "IMU_AUTO", "int": "2", + "cs": "15", "rotation": "DEG_270" }, { diff --git a/board-defaults.schema.json b/board-defaults.schema.json index 227064f..a46d093 100644 --- a/board-defaults.schema.json +++ b/board-defaults.schema.json @@ -81,9 +81,10 @@ "rotation": { "$ref": "#/$defs/IMU_ROTATION", "description": "IMU Rotation" - } + }, + "cs": { "$ref": "#/$defs/pin", "description": "CS Pin" } }, - "required": ["protocol", "imu"] + "required": ["protocol", "imu", "cs"] }, "IMU": { @@ -171,7 +172,6 @@ "SENSORS": { "type": "array", "items": { "$ref": "#/$defs/IMU" }, - "maxItems": 2, "minItems": 1, "description": "Sensors List" }, diff --git a/scripts/preprocessor.py b/scripts/preprocessor.py index 8692deb..3a49aa6 100644 --- a/scripts/preprocessor.py +++ b/scripts/preprocessor.py @@ -7,7 +7,7 @@ from typing import Union, Optional, Dict, Any, List Import("env") try: - import jsonschema + import jsonschema except: env.Execute( env.VerboseAction( @@ -37,6 +37,19 @@ def _format_raw_value(value: Any) -> str: return "true" if value else "false" return str(value) +def format_value(val: Any, typ: str) -> str: + if typ == "pin": + if isinstance(val, str) and re.search(r"[AD]", val): + return f'{val}' + else: + return _format_raw_value(val) + elif typ == "string": + return f'{val}' + elif typ in ("raw", "number"): + return _format_raw_value(val) + else: + raise ValueError(f"Value type is not supported") + def _build_board_flags(defaults: dict, board_name: str) -> List[str]: """Construct list of -D flags for one board.""" @@ -54,52 +67,65 @@ def _build_board_flags(defaults: dict, board_name: str) -> List[str]: if value is not None: args[key] = {"value": value, "type": value_type} - add("BOARD", board_name, "raw") - add("LED_PIN", values.get("LED").get("LED_PIN"), "pin") - add("LED_INVERTED", values.get("LED").get("LED_INVERTED"), "raw") + add('BOARD', board_name, 'raw') + add('LED_PIN', values.get('LED').get('LED_PIN'), 'pin') + add('LED_INVERTED', values.get('LED').get('LED_INVERTED'), 'raw') - sensors = values.get("SENSORS") + sensors = values.get('SENSORS') if sensors: + sensor_list = [] + + add('PIN_IMU_SDA', 255, 'pin') # FIXME fix the I2C Scanner so it use the sensor list and not be called when no I2C sensor + add('PIN_IMU_SCL', 255, 'pin') + add('PIN_IMU_INT_2', 255, 'pin') # FIXME: fix the CONFIG serial command so it use the sensor list + for index, sensor in enumerate(sensors): - if index == 0: - add("IMU", sensor.get("imu"), "raw") - add("PIN_IMU_INT", sensor.get("int"), "pin") - add("IMU_ROTATION", sensor.get("rotation"), "raw") - if sensor.get("protocol") == "I2C": - add("PRIMARY_IMU_ADDRESS_ONE", sensor.get("address"), "number") - if index == 1: - add("SECOND_IMU", sensor.get("imu"), "raw") - add("PIN_IMU_INT_2", sensor.get("int"), "pin") - add("SECOND_IMU_ROTATION", sensor.get("rotation"), "raw") - if sensor.get("protocol") == "I2C": - add("SECONDARY_IMU_ADDRESS_TWO", sensor.get("address"), "number") + if sensor.get('protocol') == 'I2C': + params = [ + format_value(sensor.get('imu'), 'raw'), + format_value(sensor.get('address', 'PRIMARY_IMU_ADDRESS_ONE'), 'number'), + format_value(sensor.get('rotation'), 'raw'), + f'DIRECT_WIRE({format_value(sensor.get('scl'), 'pin')}, {format_value(sensor.get('sda'), 'pin')})', + 'false' if index == 0 else 'true', + f'DIRECT_PIN({format_value(sensor.get('int', 255), 'pin')})', + '0' + ] + sensor_list.append(f'SENSOR_DESC_ENTRY({','.join(params)})') + add('PIN_IMU_SDA', sensor.get('sda'), 'pin') + add('PIN_IMU_SCL', sensor.get('scl'), 'pin') - if sensor.get("protocol") == "I2C": - add("PIN_IMU_SDA", sensor.get("sda"), "pin") - add("PIN_IMU_SCL", sensor.get("scl"), "pin") + if sensor.get('protocol') == 'SPI': + params = [ + format_value(sensor.get('imu'), 'raw'), + f'DIRECT_PIN({format_value(sensor.get('cs'), 'pin')})', + format_value(sensor.get('rotation'), 'raw'), + "DIRECT_SPI(24'000'000, MSBFIRST, SPI_MODE3)", + 'false' if index == 0 else 'true', + f'DIRECT_PIN({format_value(sensor.get('int', 255), 'pin')})', + '0' + ] + sensor_list.append(f'SENSOR_DESC_ENTRY({','.join(params)})') - battery = values.get("BATTERY") + if index == 0: # FIXME: fix the CONFIG serial command so it use the sensor list + add('PIN_IMU_INT', sensor.get('int'), 'pin') + elif index == 1: + add('PIN_IMU_INT_2', sensor.get('int'), 'pin') + add('SENSOR_DESC_LIST', f"'{' '.join(sensor_list)}'", 'raw') + + + battery = values.get('BATTERY') if battery: - add("BATTERY_MONITOR", battery.get("type"), "raw") - add("PIN_BATTERY_LEVEL", battery.get("pin", 255), "pin") - add("BATTERY_SHIELD_RESISTANCE", battery.get("shieldR", 180), "number") - add("BATTERY_SHIELD_R1", battery.get("r1", 100), "number") - add("BATTERY_SHIELD_R2", battery.get("r2", 220), "number") + add('BATTERY_MONITOR', battery.get('type'), 'raw') + add('PIN_BATTERY_LEVEL', battery.get('pin', 255), 'pin') + add('BATTERY_SHIELD_RESISTANCE', battery.get('shieldR', 180), 'number') + add('BATTERY_SHIELD_R1', battery.get('r1', 100), 'number') + add('BATTERY_SHIELD_R2', battery.get('r2', 220), 'number') parts: List[str] = [] for key, meta in args.items(): val = meta["value"] typ = meta["type"] - - if typ == "pin": - if isinstance(val, str) and re.search(r"[AD]", val): - parts.append(f"-D{key}='{val}'") - else: - parts.append(f"-D{key}={_format_raw_value(val)}") - elif typ == "string": - parts.append(f"-D{key}='{val}'") - elif typ in ("raw", "number"): - parts.append(f"-D{key}={_format_raw_value(val)}") + parts.append(f"-D{key}={format_value(val, typ)}") return parts @@ -140,18 +166,18 @@ schema_obj = _load_json("./board-defaults.schema.json") defaults_obj = _load_json("./board-defaults.json") slime_board = env.GetProjectOption("custom_slime_board", None) if slime_board: - if 'SLIMEVR_OVERRIDE_DEFAULTS' in os.environ and slime_board in defaults_obj['defaults']: - print(">>> OVERIDING BOARD DEFAULTS ", os.environ['SLIMEVR_OVERRIDE_DEFAULTS']) - defaults_obj['defaults'][slime_board]['values'] = json.loads(os.environ['SLIMEVR_OVERRIDE_DEFAULTS']) + if 'SLIMEVR_OVERRIDE_DEFAULTS' in os.environ and slime_board in defaults_obj['defaults']: + print(">>> OVERIDING BOARD DEFAULTS ", os.environ['SLIMEVR_OVERRIDE_DEFAULTS']) + defaults_obj['defaults'][slime_board]['values'] = json.loads(os.environ['SLIMEVR_OVERRIDE_DEFAULTS']) - output_flags = build_boards( - schema_obj, - defaults_obj, - slime_board, - ) - output_flags = output_flags.get(slime_board, []) if isinstance(output_flags, dict) else [] + output_flags = build_boards( + schema_obj, + defaults_obj, + slime_board, + ) + output_flags = output_flags.get(slime_board, []) if isinstance(output_flags, dict) else [] - print(">>> Appending build flags:", output_flags) - env.Append(BUILD_FLAGS=output_flags) + print(f">>> Appending build flags:\n {'\n '.join(output_flags)}") + env.Append(BUILD_FLAGS=output_flags) else: - print(">>> custom_slime_board not set - skipping") + print(">>> custom_slime_board not set - skipping") From 2970f4e38d7cbc7638bdd991f92b190b03926b60 Mon Sep 17 00:00:00 2001 From: lucas lelievre Date: Tue, 4 Nov 2025 03:13:58 +0100 Subject: [PATCH 16/25] Preprocessor: prevent shell injections (#485) --- scripts/preprocessor.py | 56 ++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/scripts/preprocessor.py b/scripts/preprocessor.py index 3a49aa6..b62f080 100644 --- a/scripts/preprocessor.py +++ b/scripts/preprocessor.py @@ -18,6 +18,8 @@ except: from jsonschema import Draft202012Validator, exceptions as jsonschema_exceptions + + def _load_json(maybe_path_or_dict: Union[str, Path, dict]) -> dict: """Load JSON file or accept dict directly.""" if isinstance(maybe_path_or_dict, dict): @@ -30,25 +32,50 @@ def _load_json(maybe_path_or_dict: Union[str, Path, dict]) -> dict: except json.JSONDecodeError as e: raise ValueError(f"Invalid JSON file {p}: {e}") +# Allow: +# - unquoted alphanumerics, underscores, dots, dashes +# - or single-quoted with optional escaped double quotes inside +VALID_DEFINE_VALUE = re.compile( + r"^(?:[A-Za-z0-9_.\-]*|'(\\\"[A-Za-z0-9_.\-\s]*\\\"|[A-Za-z0-9_.\-\s]*)')$" +) + +def _validate_define_value(value: str, key: str) -> None: + if key == "SENSOR_DESC_LIST": + return + + """Validate the formatted define value to prevent injection.""" + if not VALID_DEFINE_VALUE.fullmatch(value): + raise ValueError( + f"Invalid characters in value for {key!r}: {value!r} " + "(only letters, digits, _, ., -, spaces, and optional quoted forms like '\"text\"' allowed)" + ) def _format_raw_value(value: Any) -> str: - """Format booleans for c/cpp, otherwise str(value).""" + """Format booleans for C/C++, otherwise str(value).""" if isinstance(value, bool): return "true" if value else "false" return str(value) -def format_value(val: Any, typ: str) -> str: +def format_value(val: Any, typ: str, key: str = "") -> str: + """Format a value according to type, with built-in validation.""" if typ == "pin": if isinstance(val, str) and re.search(r"[AD]", val): - return f'{val}' + result = f'{val}' else: - return _format_raw_value(val) + result = _format_raw_value(val) + elif typ == "string": - return f'{val}' + result = f"'\\\"{val}\\\"'" + elif typ in ("raw", "number"): - return _format_raw_value(val) + result = _format_raw_value(val) + else: - raise ValueError(f"Value type is not supported") + raise ValueError(f"Value type '{typ}' is not supported") + + + _validate_define_value(result, key) + return result def _build_board_flags(defaults: dict, board_name: str) -> List[str]: @@ -85,9 +112,9 @@ def _build_board_flags(defaults: dict, board_name: str) -> List[str]: format_value(sensor.get('imu'), 'raw'), format_value(sensor.get('address', 'PRIMARY_IMU_ADDRESS_ONE'), 'number'), format_value(sensor.get('rotation'), 'raw'), - f'DIRECT_WIRE({format_value(sensor.get('scl'), 'pin')}, {format_value(sensor.get('sda'), 'pin')})', + f"DIRECT_WIRE({format_value(sensor.get('scl'), 'pin')}, {format_value(sensor.get('sda'), 'pin')})", 'false' if index == 0 else 'true', - f'DIRECT_PIN({format_value(sensor.get('int', 255), 'pin')})', + f"DIRECT_PIN({format_value(sensor.get('int', 255), 'pin')})", '0' ] sensor_list.append(f'SENSOR_DESC_ENTRY({','.join(params)})') @@ -97,14 +124,14 @@ def _build_board_flags(defaults: dict, board_name: str) -> List[str]: if sensor.get('protocol') == 'SPI': params = [ format_value(sensor.get('imu'), 'raw'), - f'DIRECT_PIN({format_value(sensor.get('cs'), 'pin')})', + f"DIRECT_PIN({format_value(sensor.get('cs'), 'pin')})", format_value(sensor.get('rotation'), 'raw'), "DIRECT_SPI(24'000'000, MSBFIRST, SPI_MODE3)", 'false' if index == 0 else 'true', - f'DIRECT_PIN({format_value(sensor.get('int', 255), 'pin')})', + f"DIRECT_PIN({format_value(sensor.get('int', 255), 'pin')})", '0' ] - sensor_list.append(f'SENSOR_DESC_ENTRY({','.join(params)})') + sensor_list.append(f"SENSOR_DESC_ENTRY({','.join(params)})") if index == 0: # FIXME: fix the CONFIG serial command so it use the sensor list add('PIN_IMU_INT', sensor.get('int'), 'pin') @@ -123,9 +150,8 @@ def _build_board_flags(defaults: dict, board_name: str) -> List[str]: parts: List[str] = [] for key, meta in args.items(): - val = meta["value"] - typ = meta["type"] - parts.append(f"-D{key}={format_value(val, typ)}") + formatted = format_value(meta["value"], meta["type"], key) + parts.append(f"-D{key}={formatted}") return parts From b6cec9cc101f79d13e9faaf9c3023b2d130a4bfd Mon Sep 17 00:00:00 2001 From: lucas lelievre Date: Wed, 5 Nov 2025 20:01:22 +0100 Subject: [PATCH 17/25] Preprocessor: Fix imu adress (#486) * Fix imu adress being wrong on extensions + fix optional tracker value to be flipped * Set max imus back to two --- board-defaults.schema.json | 1 + scripts/preprocessor.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/board-defaults.schema.json b/board-defaults.schema.json index a46d093..80fdac1 100644 --- a/board-defaults.schema.json +++ b/board-defaults.schema.json @@ -173,6 +173,7 @@ "type": "array", "items": { "$ref": "#/$defs/IMU" }, "minItems": 1, + "maxItems": 2, "description": "Sensors List" }, "LED": { diff --git a/scripts/preprocessor.py b/scripts/preprocessor.py index b62f080..d4070c3 100644 --- a/scripts/preprocessor.py +++ b/scripts/preprocessor.py @@ -110,7 +110,7 @@ def _build_board_flags(defaults: dict, board_name: str) -> List[str]: if sensor.get('protocol') == 'I2C': params = [ format_value(sensor.get('imu'), 'raw'), - format_value(sensor.get('address', 'PRIMARY_IMU_ADDRESS_ONE'), 'number'), + format_value(sensor.get('address', 'PRIMARY_IMU_ADDRESS_ONE' if index == 0 else 'SECONDARY_IMU_ADDRESS_TWO'), 'number'), format_value(sensor.get('rotation'), 'raw'), f"DIRECT_WIRE({format_value(sensor.get('scl'), 'pin')}, {format_value(sensor.get('sda'), 'pin')})", 'false' if index == 0 else 'true', From c84e898d1e89668789732aef50d73d24a63d15e7 Mon Sep 17 00:00:00 2001 From: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> Date: Wed, 5 Nov 2025 20:01:43 +0100 Subject: [PATCH 18/25] fix build on python List[str]: f"DIRECT_PIN({format_value(sensor.get('int', 255), 'pin')})", '0' ] - sensor_list.append(f'SENSOR_DESC_ENTRY({','.join(params)})') + sensor_list.append(f"SENSOR_DESC_ENTRY({','.join(params)})") add('PIN_IMU_SDA', sensor.get('sda'), 'pin') add('PIN_IMU_SCL', sensor.get('scl'), 'pin') @@ -203,7 +203,8 @@ if slime_board: ) output_flags = output_flags.get(slime_board, []) if isinstance(output_flags, dict) else [] - print(f">>> Appending build flags:\n {'\n '.join(output_flags)}") + separator = '\n ' + print(f">>> Appending build flags:\n {separator.join(output_flags)}") env.Append(BUILD_FLAGS=output_flags) else: print(">>> custom_slime_board not set - skipping") From 79d2796039dfef222d448985a56fd1eb5df5c7cf Mon Sep 17 00:00:00 2001 From: gorbit99 Date: Fri, 7 Nov 2025 08:30:13 +0100 Subject: [PATCH 19/25] Fix sensor toggles causing a crash on 0.7.0 (#487) * Fix sensor toggles causing a crash on 0.7.0 * Formatting --- src/configuration/Configuration.cpp | 48 ++++++++++++++++++++++++++--- src/sensors/SensorToggles.cpp | 17 ++++++---- src/sensors/SensorToggles.h | 17 +++++++--- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/configuration/Configuration.cpp b/src/configuration/Configuration.cpp index 402056a..efc4f30 100644 --- a/src/configuration/Configuration.cpp +++ b/src/configuration/Configuration.cpp @@ -25,13 +25,18 @@ #include +#include +#include + #include "../FSHelper.h" #include "consts.h" +#include "sensors/SensorToggles.h" #include "utils.h" #define DIR_CALIBRATIONS "/calibrations" #define DIR_TEMPERATURE_CALIBRATIONS "/tempcalibrations" -#define DIR_TOGGLES "/toggles" +#define DIR_TOGGLES_OLD "/toggles" +#define DIR_TOGGLES "/sensortoggles" namespace SlimeVR::Configuration { void Configuration::setup() { @@ -123,7 +128,8 @@ void Configuration::save() { m_Logger.trace("Saving sensor toggle state for %d", i); file = LittleFS.open(path, "w"); - file.write((uint8_t*)&m_SensorToggles[i], sizeof(SensorToggleState)); + auto toggleValues = m_SensorToggles[i].getValues(); + file.write((uint8_t*)&toggleValues, sizeof(SensorToggleValues)); file.close(); } @@ -133,6 +139,18 @@ void Configuration::save() { file.close(); } + // Clean up old toggles directory + if (LittleFS.exists(DIR_TOGGLES_OLD)) { + char path[17] = DIR_TOGGLES_OLD; + char* end = path + strlen(DIR_TOGGLES_OLD); + Utils::forEachFile(DIR_TOGGLES_OLD, [&](SlimeVR::Utils::File file) { + sprintf(end, "/%s", file.name()); + LittleFS.remove(path); + file.close(); + }); + LittleFS.rmdir(DIR_TOGGLES_OLD); + } + m_Logger.debug("Saved configuration"); } @@ -226,14 +244,34 @@ void Configuration::loadSensors() { setSensor(sensorId, sensorConfig); }); + if (LittleFS.exists(DIR_TOGGLES_OLD)) { + SlimeVR::Utils::forEachFile(DIR_TOGGLES_OLD, [&](SlimeVR::Utils::File f) { + SensorToggleValues values; + // Migration for pre 0.7.0 togglestate, the values started at offset 20 and + // there were 3 of them + f.seek(20); + f.read(reinterpret_cast(&values), 3); + + uint8_t sensorId = strtoul(f.name(), nullptr, 10); + m_Logger.debug("Found sensor toggle state at index %d", sensorId); + + setSensorToggles(sensorId, SensorToggleState{values}); + }); + } + SlimeVR::Utils::forEachFile(DIR_TOGGLES, [&](SlimeVR::Utils::File f) { - SensorToggleState sensorToggleState; - f.read((uint8_t*)&sensorToggleState, sizeof(SensorToggleState)); + if (f.size() > sizeof(SensorToggleValues)) { + return; + } + SensorToggleValues values; + // With the magic of C++ default initialization, the rest of the values should + // be their default after reading + f.read(reinterpret_cast(&values), f.size()); uint8_t sensorId = strtoul(f.name(), nullptr, 10); m_Logger.debug("Found sensor toggle state at index %d", sensorId); - setSensorToggles(sensorId, sensorToggleState); + setSensorToggles(sensorId, SensorToggleState{values}); }); } diff --git a/src/sensors/SensorToggles.cpp b/src/sensors/SensorToggles.cpp index fe59e7e..848a65d 100644 --- a/src/sensors/SensorToggles.cpp +++ b/src/sensors/SensorToggles.cpp @@ -1,15 +1,18 @@ #include "SensorToggles.h" +SensorToggleState::SensorToggleState(SensorToggleValues values) + : values{values} {} + void SensorToggleState::setToggle(SensorToggles toggle, bool state) { switch (toggle) { case SensorToggles::MagEnabled: - magEnabled = state; + values.magEnabled = state; break; case SensorToggles::CalibrationEnabled: - calibrationEnabled = state; + values.calibrationEnabled = state; break; case SensorToggles::TempGradientCalibrationEnabled: - tempGradientCalibrationEnabled = state; + values.tempGradientCalibrationEnabled = state; break; } } @@ -17,11 +20,11 @@ void SensorToggleState::setToggle(SensorToggles toggle, bool state) { bool SensorToggleState::getToggle(SensorToggles toggle) const { switch (toggle) { case SensorToggles::MagEnabled: - return magEnabled; + return values.magEnabled; case SensorToggles::CalibrationEnabled: - return calibrationEnabled; + return values.calibrationEnabled; case SensorToggles::TempGradientCalibrationEnabled: - return tempGradientCalibrationEnabled; + return values.tempGradientCalibrationEnabled; } return false; } @@ -32,6 +35,8 @@ void SensorToggleState::onToggleChange( this->callback = callback; } +SensorToggleValues SensorToggleState::getValues() const { return values; } + void SensorToggleState::emitToggleChange(SensorToggles toggle, bool state) const { if (callback) { (*callback)(toggle, state); diff --git a/src/sensors/SensorToggles.h b/src/sensors/SensorToggles.h index c10aad2..a7e4cdf 100644 --- a/src/sensors/SensorToggles.h +++ b/src/sensors/SensorToggles.h @@ -35,8 +35,17 @@ enum class SensorToggles : uint16_t { TempGradientCalibrationEnabled = 3, }; +struct SensorToggleValues { + bool magEnabled = !USE_6_AXIS; + bool calibrationEnabled = true; + bool tempGradientCalibrationEnabled + = false; // disable by default, it is not clear that it really helps +}; + class SensorToggleState { public: + SensorToggleState() = default; + explicit SensorToggleState(SensorToggleValues values); void setToggle(SensorToggles toggle, bool state); [[nodiscard]] bool getToggle(SensorToggles toggle) const; @@ -44,12 +53,12 @@ public: static const char* toggleToString(SensorToggles toggle); + [[nodiscard]] SensorToggleValues getValues() const; + private: std::optional> callback; + void emitToggleChange(SensorToggles toggle, bool state) const; - bool magEnabled = !USE_6_AXIS; - bool calibrationEnabled = true; - bool tempGradientCalibrationEnabled - = false; // disable by default, it is not clear that it really helps + SensorToggleValues values; }; From 2a66d12031e348245a37bf854803e09a9a679d9b Mon Sep 17 00:00:00 2001 From: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> Date: Sat, 22 Nov 2025 09:26:05 +0100 Subject: [PATCH 20/25] Move heavy string operation for static into compiler time (#495) move heavy string operation for static into compiler time --- src/serial/serialcommands.cpp | 47 ++++++++++++----------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/serial/serialcommands.cpp b/src/serial/serialcommands.cpp index 124c7df..08f9f7f 100644 --- a/src/serial/serialcommands.cpp +++ b/src/serial/serialcommands.cpp @@ -44,6 +44,21 @@ #define CALLBACK_SIZE 6 // Default callback size #endif +#if defined(VENDOR_URL) && defined(VENDOR_NAME) && defined(PRODUCT_NAME) \ + && defined(UPDATE_ADDRESS) && defined(UPDATE_NAME) +constexpr const char* FULL_VENDOR_STR + = "Vendor: " VENDOR_NAME " (" VENDOR_URL "), product: " PRODUCT_NAME + ", firmware update url: " UPDATE_ADDRESS ", name: " UPDATE_NAME; +#elif defined(VENDOR_URL) && defined(VENDOR_NAME) && defined(PRODUCT_NAME) +constexpr const char* FULL_VENDOR_STR + = "Vendor: " VENDOR_NAME " (" VENDOR_URL "), product: " PRODUCT_NAME; +#elif defined(VENDOR_NAME) && defined(PRODUCT_NAME) +constexpr const char* FULL_VENDOR_STR + = "Vendor: " VENDOR_NAME ", product: " PRODUCT_NAME; +#else +constexpr const char* FULL_VENDOR_STR = "Vendor: Unknown, product: Unknown"; +#endif + namespace SerialCommands { SlimeVR::Logging::Logger logger("SerialCommands"); @@ -165,37 +180,7 @@ void printState() { wifiNetwork.getWiFiState() ); - char vendorBuffer[512]; - size_t writtenLength; - - if (strlen(VENDOR_URL) == 0) { - sprintf( - vendorBuffer, - "Vendor: %s, product: %s%n", - VENDOR_NAME, - PRODUCT_NAME, - &writtenLength - ); - } else { - sprintf( - vendorBuffer, - "Vendor: %s (%s), product: %s%n", - VENDOR_NAME, - VENDOR_URL, - PRODUCT_NAME, - &writtenLength - ); - } - - if (strlen(UPDATE_ADDRESS) > 0 && strlen(UPDATE_NAME) > 0) { - sprintf( - vendorBuffer + writtenLength, - ", firmware update url: %s, name: %s", - UPDATE_ADDRESS, - UPDATE_NAME - ); - } - logger.info("%s", vendorBuffer); + logger.info("%s", FULL_VENDOR_STR); for (auto& sensor : sensorManager.getSensors()) { logger.info( From 060df4baecfe84a27d9251f8f7c831d3e8f9a8d5 Mon Sep 17 00:00:00 2001 From: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> Date: Thu, 27 Nov 2025 19:25:40 +0100 Subject: [PATCH 21/25] fix_uploadspeed for platformio (#492) fix_uploadspeed --- platformio.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platformio.ini b/platformio.ini index a0dc9ff..e3ccffb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -72,21 +72,25 @@ build_unflags = -Os -std=gnu++11 -std=gnu++17 platform = espressif8266 @ 4.2.1 board = esp12e custom_slime_board = BOARD_WEMOSD1MINI +upload_speed = 921600 [env:BOARD_NODEMCU] platform = espressif8266 @ 4.2.1 board = esp12e custom_slime_board = BOARD_NODEMCU +upload_speed = 921600 [env:BOARD_TTGO_TBASE] platform = espressif8266 @ 4.2.1 board = esp12e custom_slime_board = BOARD_TTGO_TBASE +upload_speed = 921600 [env:BOARD_WEMOSWROOM02] platform = espressif8266 @ 4.2.1 board = esp12e custom_slime_board = BOARD_WEMOSWROOM02 +upload_speed = 921600 [env:BOARD_WROOM32] platform = espressif32 @ 6.7.0 @@ -101,6 +105,7 @@ platform = espressif8266 @ 4.2.1 board = esp01_1m board_build.arduino.ldscript = "eagle.flash.1m64.ld" custom_slime_board = BOARD_ESP01 +upload_speed = 921600 [env:BOARD_LOLIN_C3_MINI] platform = espressif32 @ 6.7.0 From 72ce7130797af17d1b3d8a78748250b23d6cdf19 Mon Sep 17 00:00:00 2001 From: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> Date: Thu, 27 Nov 2025 19:25:54 +0100 Subject: [PATCH 22/25] fix bmi270 missing in flasher (#496) --- board-defaults.schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/board-defaults.schema.json b/board-defaults.schema.json index 80fdac1..e5cfcfb 100644 --- a/board-defaults.schema.json +++ b/board-defaults.schema.json @@ -27,6 +27,7 @@ "IMU_BMI160", "IMU_ICM20948", "IMU_ICM42688", + "IMU_BMI270", "IMU_LSM6DS3TRC", "IMU_LSM6DSV", "IMU_LSM6DSO", From afd376c427637010a7e56bfe6703a6f2fd325d17 Mon Sep 17 00:00:00 2001 From: gorbit99 Date: Thu, 27 Nov 2025 19:28:26 +0100 Subject: [PATCH 23/25] Fix sampling rate miscalibration problem (#494) --- src/sensors/softfusion/CalibrationBase.h | 1 + src/sensors/softfusion/drivers/bmi160.h | 4 +++- src/sensors/softfusion/drivers/bmi270.h | 4 +++- src/sensors/softfusion/drivers/icm42688.h | 4 +++- src/sensors/softfusion/drivers/icm45base.h | 10 +++++---- .../softfusion/drivers/lsm6ds-common.h | 3 ++- src/sensors/softfusion/drivers/lsm6ds3trc.h | 6 +++-- src/sensors/softfusion/drivers/lsm6dso.h | 4 ++-- src/sensors/softfusion/drivers/lsm6dsr.h | 4 ++-- src/sensors/softfusion/drivers/lsm6dsv.h | 4 ++-- src/sensors/softfusion/drivers/mpu6050.h | 8 ++++--- .../runtimecalibration/CalibrationStep.h | 3 +++ .../runtimecalibration/RuntimeCalibration.h | 22 ++++++++++++++----- .../SampleRateCalibrationStep.h | 8 +++++++ src/sensors/softfusion/softfusionsensor.h | 5 ++++- 15 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/sensors/softfusion/CalibrationBase.h b/src/sensors/softfusion/CalibrationBase.h index d132a36..e5c4b57 100644 --- a/src/sensors/softfusion/CalibrationBase.h +++ b/src/sensors/softfusion/CalibrationBase.h @@ -87,6 +87,7 @@ public: virtual const uint8_t* getMotionlessCalibrationData() = 0; + virtual void signalOverwhelmed() {} virtual void provideAccelSample(const RawSensorT accelSample[3]) {} virtual void provideGyroSample(const RawSensorT gyroSample[3]) {} virtual void provideTempSample(float tempSample) {} diff --git a/src/sensors/softfusion/drivers/bmi160.h b/src/sensors/softfusion/drivers/bmi160.h index d41553c..03090bb 100644 --- a/src/sensors/softfusion/drivers/bmi160.h +++ b/src/sensors/softfusion/drivers/bmi160.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -176,7 +177,7 @@ struct BMI160 { return to_ret; } - void bulkRead(DriverCallbacks&& callbacks) { + bool bulkRead(DriverCallbacks&& callbacks) { const auto fifo_bytes = m_RegisterInterface.readReg16(Regs::FifoLength) & 0x7FF; const auto bytes_to_read = std::min( @@ -219,6 +220,7 @@ struct BMI160 { } } } + return static_cast(fifo_bytes) > static_cast(bytes_to_read); } }; diff --git a/src/sensors/softfusion/drivers/bmi270.h b/src/sensors/softfusion/drivers/bmi270.h index eabea7a..47406ff 100644 --- a/src/sensors/softfusion/drivers/bmi270.h +++ b/src/sensors/softfusion/drivers/bmi270.h @@ -428,7 +428,7 @@ struct BMI270 { return to_ret; } - void bulkRead(DriverCallbacks&& callbacks) { + bool bulkRead(DriverCallbacks&& callbacks) { const auto fifo_bytes = m_RegisterInterface.readReg16(Regs::FifoCount); const auto bytes_to_read = std::min( @@ -482,6 +482,8 @@ struct BMI270 { } } } + + return fifo_bytes > bytes_to_read; } }; diff --git a/src/sensors/softfusion/drivers/icm42688.h b/src/sensors/softfusion/drivers/icm42688.h index 25fa1df..afcebc5 100644 --- a/src/sensors/softfusion/drivers/icm42688.h +++ b/src/sensors/softfusion/drivers/icm42688.h @@ -152,7 +152,7 @@ struct ICM42688 { return true; } - void bulkRead(DriverCallbacks&& callbacks) { + bool bulkRead(DriverCallbacks&& callbacks) { const auto fifo_bytes = m_RegisterInterface.readReg16(Regs::FifoCount); std::array read_buffer; // max 8 readings @@ -197,6 +197,8 @@ struct ICM42688 { ); } } + + return fifo_bytes > bytes_to_read; } }; diff --git a/src/sensors/softfusion/drivers/icm45base.h b/src/sensors/softfusion/drivers/icm45base.h index 977eac4..3993e84 100644 --- a/src/sensors/softfusion/drivers/icm45base.h +++ b/src/sensors/softfusion/drivers/icm45base.h @@ -238,13 +238,13 @@ struct ICM45Base { // stack overflow and panic std::vector read_buffer; - void bulkRead(DriverCallbacks&& callbacks) { + bool bulkRead(DriverCallbacks&& callbacks) { constexpr int16_t InvalidReading = -32768; size_t fifo_packets = m_RegisterInterface.readReg16(BaseRegs::FifoCount); if (fifo_packets <= 1) { - return; + return false; } // AN-000364 @@ -264,9 +264,9 @@ struct ICM45Base { // can cause FIFO data corruption, from happening. --fifo_packets; - fifo_packets = std::min(fifo_packets, MaxReadings); + auto packets_to_read = std::min(fifo_packets, MaxReadings); - size_t bytes_to_read = fifo_packets * FullFifoEntrySize; + size_t bytes_to_read = packets_to_read * FullFifoEntrySize; m_RegisterInterface .readBytes(BaseRegs::FifoData, bytes_to_read, read_buffer.data()); @@ -307,6 +307,8 @@ struct ICM45Base { callbacks.processTempSample(static_cast(entry.temp), TempTs); } } + + return fifo_packets > MaxReadings; } template diff --git a/src/sensors/softfusion/drivers/lsm6ds-common.h b/src/sensors/softfusion/drivers/lsm6ds-common.h index edcc620..32aabda 100644 --- a/src/sensors/softfusion/drivers/lsm6ds-common.h +++ b/src/sensors/softfusion/drivers/lsm6ds-common.h @@ -55,7 +55,7 @@ struct LSM6DSOutputHandler { static constexpr size_t FullFifoEntrySize = sizeof(FifoEntryAligned) + 1; template - void bulkRead( + bool bulkRead( DriverCallbacks&& callbacks, float GyrTs, float AccTs, @@ -103,6 +103,7 @@ struct LSM6DSOutputHandler { break; } } + return fifo_bytes > bytes_to_read; } }; diff --git a/src/sensors/softfusion/drivers/lsm6ds3trc.h b/src/sensors/softfusion/drivers/lsm6ds3trc.h index db2e79b..dc2b185 100644 --- a/src/sensors/softfusion/drivers/lsm6ds3trc.h +++ b/src/sensors/softfusion/drivers/lsm6ds3trc.h @@ -122,14 +122,14 @@ struct LSM6DS3TRC { return true; } - void bulkRead(DriverCallbacks&& callbacks) { + bool bulkRead(DriverCallbacks&& callbacks) { const auto read_result = m_RegisterInterface.readReg16(Regs::FifoStatus); if (read_result & 0x4000) { // overrun! // disable and re-enable fifo to clear it m_Logger.debug("Fifo overrun, resetting..."); m_RegisterInterface.writeReg(Regs::FifoCtrl5::reg, 0); m_RegisterInterface.writeReg(Regs::FifoCtrl5::reg, Regs::FifoCtrl5::value); - return; + return true; } const auto unread_entries = read_result & 0x7ff; constexpr auto single_measurement_words = 6; @@ -156,6 +156,8 @@ struct LSM6DS3TRC { callbacks.processAccelSample(&read_buffer[i + 3], AccTs); callbacks.processTempSample(read_buffer[i + 9], TempTs); } + + return static_cast(unread_entries) > read_buffer.size(); } }; // namespace SlimeVR::Sensors::SoftFusion::Drivers diff --git a/src/sensors/softfusion/drivers/lsm6dso.h b/src/sensors/softfusion/drivers/lsm6dso.h index 4ab490d..760e3f3 100644 --- a/src/sensors/softfusion/drivers/lsm6dso.h +++ b/src/sensors/softfusion/drivers/lsm6dso.h @@ -117,8 +117,8 @@ struct LSM6DSO : LSM6DSOutputHandler { return true; } - void bulkRead(DriverCallbacks&& callbacks) { - LSM6DSOutputHandler::template bulkRead( + bool bulkRead(DriverCallbacks&& callbacks) { + return LSM6DSOutputHandler::template bulkRead( std::move(callbacks), GyrTs, AccTs, diff --git a/src/sensors/softfusion/drivers/lsm6dsr.h b/src/sensors/softfusion/drivers/lsm6dsr.h index 820c57f..d1fc089 100644 --- a/src/sensors/softfusion/drivers/lsm6dsr.h +++ b/src/sensors/softfusion/drivers/lsm6dsr.h @@ -115,8 +115,8 @@ struct LSM6DSR : LSM6DSOutputHandler { return true; } - void bulkRead(DriverCallbacks&& callbacks) { - LSM6DSOutputHandler::template bulkRead( + bool bulkRead(DriverCallbacks&& callbacks) { + return LSM6DSOutputHandler::template bulkRead( std::move(callbacks), GyrTs, AccTs, diff --git a/src/sensors/softfusion/drivers/lsm6dsv.h b/src/sensors/softfusion/drivers/lsm6dsv.h index ec16092..36b59a4 100644 --- a/src/sensors/softfusion/drivers/lsm6dsv.h +++ b/src/sensors/softfusion/drivers/lsm6dsv.h @@ -131,8 +131,8 @@ struct LSM6DSV : LSM6DSOutputHandler { return true; } - void bulkRead(DriverCallbacks&& callbacks) { - LSM6DSOutputHandler::template bulkRead( + bool bulkRead(DriverCallbacks&& callbacks) { + return LSM6DSOutputHandler::template bulkRead( std::move(callbacks), GyrTs, AccTs, diff --git a/src/sensors/softfusion/drivers/mpu6050.h b/src/sensors/softfusion/drivers/mpu6050.h index 3d544a0..1b3bea3 100644 --- a/src/sensors/softfusion/drivers/mpu6050.h +++ b/src/sensors/softfusion/drivers/mpu6050.h @@ -176,7 +176,7 @@ struct MPU6050 { return result; } - void bulkRead(DriverCallbacks&& callbacks) { + bool bulkRead(DriverCallbacks&& callbacks) { const auto status = m_RegisterInterface.readReg(Regs::IntStatus); if (status & (1 << MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) { @@ -184,7 +184,7 @@ struct MPU6050 { // This necessitates a reset m_Logger.debug("Fifo overrun, resetting..."); resetFIFO(); - return; + return true; } std::array @@ -194,7 +194,7 @@ struct MPU6050 { auto readBytes = min(static_cast(byteCount), readBuffer.size()) / sizeof(FifoSample) * sizeof(FifoSample); if (!readBytes) { - return; + return false; } m_RegisterInterface.readBytes(Regs::FifoData, readBytes, readBuffer.data()); @@ -213,6 +213,8 @@ struct MPU6050 { xyz[2] = MPU6050_FIFO_VALUE(sample, gyro_z); callbacks.processGyroSample(xyz, GyrTs); } + + return byteCount > readBytes; } }; // namespace SlimeVR::Sensors::SoftFusion::Drivers diff --git a/src/sensors/softfusion/runtimecalibration/CalibrationStep.h b/src/sensors/softfusion/runtimecalibration/CalibrationStep.h index 27c6f08..d07fd16 100644 --- a/src/sensors/softfusion/runtimecalibration/CalibrationStep.h +++ b/src/sensors/softfusion/runtimecalibration/CalibrationStep.h @@ -46,6 +46,9 @@ public: virtual void cancel() = 0; virtual bool requiresRest() { return true; } + // Signals that the sensor had more packets than the MCU could read, which can + // compromise calibration + virtual void signalOverwhelmed() {} virtual void processAccelSample(const SensorRawT accelSample[3]) {} virtual void processGyroSample(const SensorRawT accelSample[3]) {} virtual void processTempSample(float tempSample) {} diff --git a/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h b/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h index fe139b2..16be470 100644 --- a/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h +++ b/src/sensors/softfusion/runtimecalibration/RuntimeCalibration.h @@ -142,10 +142,14 @@ public: switch (result) { case CalibrationStep::TickResult::DONE: + if (nextCalibrationStep == CalibrationStepEnum::SAMPLING_RATE) { + stepCalibrationForward(true, false); + break; + } stepCalibrationForward(); break; case CalibrationStep::TickResult::SKIP: - stepCalibrationForward(false); + stepCalibrationForward(false, false); break; case CalibrationStep::TickResult::CONTINUE: break; @@ -182,6 +186,12 @@ public: return activeCalibration.MotionlessData; } + void signalOverwhelmed() final { + if (isCalibrating) { + currentStep->signalOverwhelmed(); + } + } + void provideAccelSample(const RawSensorT accelSample[3]) final { if (isCalibrating) { currentStep->processAccelSample(accelSample); @@ -247,7 +257,7 @@ private: } } - void stepCalibrationForward(bool save = true) { + void stepCalibrationForward(bool print = true, bool save = true) { currentStep->cancel(); switch (nextCalibrationStep) { case CalibrationStepEnum::NONE: @@ -255,14 +265,14 @@ private: case CalibrationStepEnum::SAMPLING_RATE: nextCalibrationStep = CalibrationStepEnum::MOTIONLESS; currentStep = &motionlessCalibrationStep; - if (save) { + if (print) { printCalibration(CalibrationPrintFlags::TIMESTEPS); } break; case CalibrationStepEnum::MOTIONLESS: nextCalibrationStep = CalibrationStepEnum::GYRO_BIAS; currentStep = &gyroBiasCalibrationStep; - if (save) { + if (print) { printCalibration(CalibrationPrintFlags::MOTIONLESS); } break; @@ -274,7 +284,7 @@ private: currentStep = &gyroBiasCalibrationStep; } - if (save) { + if (print) { printCalibration(CalibrationPrintFlags::GYRO_BIAS); } break; @@ -282,7 +292,7 @@ private: nextCalibrationStep = CalibrationStepEnum::GYRO_BIAS; currentStep = &gyroBiasCalibrationStep; - if (save) { + if (print) { printCalibration(CalibrationPrintFlags::ACCEL_BIAS); } diff --git a/src/sensors/softfusion/runtimecalibration/SampleRateCalibrationStep.h b/src/sensors/softfusion/runtimecalibration/SampleRateCalibrationStep.h index 26e43b9..3453e1d 100644 --- a/src/sensors/softfusion/runtimecalibration/SampleRateCalibrationStep.h +++ b/src/sensors/softfusion/runtimecalibration/SampleRateCalibrationStep.h @@ -67,6 +67,14 @@ public: void cancel() override final { calibrationData.reset(); } bool requiresRest() override final { return false; } + void signalOverwhelmed() override final { + // Not good, restart + calibrationData.value().accelSamples = 0; + calibrationData.value().gyroSamples = 0; + calibrationData.value().tempSamples = 0; + calibrationData.value().startMillis = millis(); + } + void processAccelSample(const SensorRawT accelSample[3]) override final { calibrationData.value().accelSamples++; } diff --git a/src/sensors/softfusion/softfusionsensor.h b/src/sensors/softfusion/softfusionsensor.h index 49d37c3..f260c7b 100644 --- a/src/sensors/softfusion/softfusionsensor.h +++ b/src/sensors/softfusion/softfusionsensor.h @@ -241,7 +241,7 @@ public: constexpr uint32_t sendInterval = 1.0f / maxSendRateHz * 1e6f; elapsed = now - m_lastRotationPacketSent; if (elapsed >= sendInterval) { - m_sensor.bulkRead({ + auto overwhelmed = m_sensor.bulkRead({ [&](const auto sample[3], float AccTs) { processAccelSample(sample, AccTs); }, @@ -252,6 +252,9 @@ public: processTempSample(sample, TempTs); }, }); + if (overwhelmed) { + calibrator.signalOverwhelmed(); + } if (!m_fusion.isUpdated()) { checkSensorTimeout(); return; From cc42ad0aa97e022d96afd0be51fa003944de8ed3 Mon Sep 17 00:00:00 2001 From: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> Date: Thu, 27 Nov 2025 19:29:01 +0100 Subject: [PATCH 24/25] fix no broadcast after disconnect (#491) fix no broadcast after disconnect After a disconnect the trackers did not send a broadcast to discover the server but did directly try to send it to the server. So if for some reason the server did change its ip address the tracker had to be rebooted. --- src/network/connection.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/network/connection.cpp b/src/network/connection.cpp index edada9f..b7a279f 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -620,6 +620,9 @@ void Connection::reset() { m_UDP.begin(m_ServerPort); + // Reset server address to broadcast if disconnected + m_ServerHost = IPAddress(255, 255, 255, 255); + statusManager.setStatus(SlimeVR::Status::SERVER_CONNECTING, true); } @@ -650,6 +653,9 @@ void Connection::update() { ); m_Logger.warn("Connection to server timed out"); + // Reset server address to broadcast if disconnected + m_ServerHost = IPAddress(255, 255, 255, 255); + return; } From e6af00b1615482ac92b9771abde84c27d21d30cb Mon Sep 17 00:00:00 2001 From: Meia <71262281+kounocom@users.noreply.github.com> Date: Fri, 28 Nov 2025 01:53:20 +0200 Subject: [PATCH 25/25] Fix LSM* Accelerometer Sensitivity (#499) --- src/sensors/softfusion/drivers/lsm6ds3trc.h | 2 +- src/sensors/softfusion/drivers/lsm6dso.h | 2 +- src/sensors/softfusion/drivers/lsm6dsr.h | 2 +- src/sensors/softfusion/drivers/lsm6dsv.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sensors/softfusion/drivers/lsm6ds3trc.h b/src/sensors/softfusion/drivers/lsm6ds3trc.h index dc2b185..4bdaa9b 100644 --- a/src/sensors/softfusion/drivers/lsm6ds3trc.h +++ b/src/sensors/softfusion/drivers/lsm6ds3trc.h @@ -54,7 +54,7 @@ struct LSM6DS3TRC { static constexpr float TempTs = 1.0 / TempFreq; static constexpr float GyroSensitivity = 28.571428571f; - static constexpr float AccelSensitivity = 4098.360655738f; + static constexpr float AccelSensitivity = 1000 / 0.122f; static constexpr float TemperatureBias = 25.0f; static constexpr float TemperatureSensitivity = 256.0f; diff --git a/src/sensors/softfusion/drivers/lsm6dso.h b/src/sensors/softfusion/drivers/lsm6dso.h index 760e3f3..9702631 100644 --- a/src/sensors/softfusion/drivers/lsm6dso.h +++ b/src/sensors/softfusion/drivers/lsm6dso.h @@ -53,7 +53,7 @@ struct LSM6DSO : LSM6DSOutputHandler { static constexpr float TempTs = 1.0 / TempFreq; static constexpr float GyroSensitivity = 1000 / 35.0f; - static constexpr float AccelSensitivity = 1000 / 0.244f; + static constexpr float AccelSensitivity = 1000 / 0.122f; static constexpr float TemperatureBias = 25.0f; static constexpr float TemperatureSensitivity = 256.0f; diff --git a/src/sensors/softfusion/drivers/lsm6dsr.h b/src/sensors/softfusion/drivers/lsm6dsr.h index d1fc089..bfd45b3 100644 --- a/src/sensors/softfusion/drivers/lsm6dsr.h +++ b/src/sensors/softfusion/drivers/lsm6dsr.h @@ -51,7 +51,7 @@ struct LSM6DSR : LSM6DSOutputHandler { static constexpr float TempTs = 1.0 / TempFreq; static constexpr float GyroSensitivity = 1000 / 35.0f; - static constexpr float AccelSensitivity = 1000 / 0.244f; + static constexpr float AccelSensitivity = 1000 / 0.122f; static constexpr float TemperatureBias = 25.0f; static constexpr float TemperatureSensitivity = 256.0f; diff --git a/src/sensors/softfusion/drivers/lsm6dsv.h b/src/sensors/softfusion/drivers/lsm6dsv.h index 36b59a4..df4916a 100644 --- a/src/sensors/softfusion/drivers/lsm6dsv.h +++ b/src/sensors/softfusion/drivers/lsm6dsv.h @@ -52,7 +52,7 @@ struct LSM6DSV : LSM6DSOutputHandler { static constexpr float TempTs = 1.0 / TempFreq; static constexpr float GyroSensitivity = 1000 / 35.0f; - static constexpr float AccelSensitivity = 1000 / 0.244f; + static constexpr float AccelSensitivity = 1000 / 0.122f; static constexpr float TemperatureBias = 25.0f; static constexpr float TemperatureSensitivity = 256.0f;