From 7be246a230949c053f69c1888015218ceb444000 Mon Sep 17 00:00:00 2001 From: tron Date: Sat, 25 Apr 2026 12:14:24 -0700 Subject: [PATCH] Fix Peloton serial homing range and post-homing starting gear Peloton serial users could not shift up in Zwift free-ride mode after the Jan 2 2026 commit that hardcoded gear 8 as the post-homing position. The root cause is two gaps in the serial Peloton homing path: 1. pelotonConnected() set the shift-blocking bounds (minResistance / maxResistance) but never set resistance.setMin/setMax, so _findFTMSHome() always saw resistance.getMax() == 0 and was never called for serial Peloton users. 2. goHome() required connectedPowerMeter != NONE to take the resistance- feedback homing path, but serial Peloton has no BLE power meter. It fell through to StallGuard sensorless detection, which fires prematurely due to the Peloton's continuously-increasing magnetic resistance, leaving hMax at ~50% of real physical travel. Fixes: - pelotonConnected(): also call resistance.setMin/setMax so that _findFTMSHome() receives valid [5, 98] resistance targets. - goHome(): extend FTMS homing condition to include pelotonIsConnected, routing serial Peloton through resistance-feedback homing. - BLE_Client startup homing: replace hardcoded setShifterPosition(8) with hMax / (2 * shiftStep), clamped >= 0, placing the motor at the center of the actual calibrated travel range regardless of bike type. After flashing, a Zwift spindown calibration will establish the correct full-range hMax via resistance feedback and persist it to config. Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 10 ++++++++++ src/BLE_Client.cpp | 3 ++- src/Main.cpp | 6 ++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20bc482c..618dff43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added ### Changed +- Fix Peloton serial homing to use resistance feedback instead of StallGuard sensorless detection. + StallGuard fired prematurely on Peloton's continuously-increasing magnetic resistance, causing + hMax to be set at ~50% of the actual physical travel range. pelotonConnected() now sets + resistance.setMin/setMax (the homing target bounds) so that _findFTMSHome() receives valid + targets, and goHome() now includes ss2k->pelotonIsConnected in its FTMS homing condition so + Peloton serial users take the resistance-feedback path rather than falling through to StallGuard. +- Replace hardcoded post-homing gear 8 with a calculated midpoint (hMax / (2 * shiftStep)). + The hardcoded value assumed a correctly-ranged hMax; for Peloton serial users whose hMax was + only ~50% of physical range, starting at gear 8 left no room to shift up. The calculated + value places the motor at the center of the actual calibrated travel range. ### Hardware diff --git a/src/BLE_Client.cpp b/src/BLE_Client.cpp index ad35653f..88c3a820 100644 --- a/src/BLE_Client.cpp +++ b/src/BLE_Client.cpp @@ -239,7 +239,8 @@ void bleClientTask(void* pvParameters) { ss2k->goHome(true); } else { // Startup Homing ss2k->goHome(false); - rtConfig->setShifterPosition(8); // Set to middle position after homing on startup + int startGear = (userConfig->getShiftStep() > 0) ? (userConfig->getHMax() / (2 * userConfig->getShiftStep())) : 4; + rtConfig->setShifterPosition(startGear > 0 ? startGear : 0); // Start at ~midpoint of calibrated travel range } spinBLEServer.spinDownFlag = 0; } diff --git a/src/Main.cpp b/src/Main.cpp index e55d4cac..7a5b31e3 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -812,8 +812,8 @@ void SS2K::goHome(bool bothDirections) { } } - // if we're using real resistance from a FTMS bike, find those values for the reported min and max resistance instead of using hard stops. - if (!rtConfig->resistance.getSimulate() && userConfig->getConnectedPowerMeter() != NONE && rtConfig->resistance.getMax() > 0) { + // if we're using real resistance from a FTMS bike or Peloton serial, use resistance feedback for homing instead of hard stops. + if (!rtConfig->resistance.getSimulate() && (userConfig->getConnectedPowerMeter() != NONE || ss2k->pelotonIsConnected) && rtConfig->resistance.getMax() > 0) { ss2k->_findFTMSHome(bothDirections); if (rtConfig->getHomed()) { fitnessMachineService.spinDown(FitnessMachineStatus::SpinDown_Success); @@ -955,6 +955,8 @@ bool SS2K::pelotonConnected() { if (millis() - rtConfig->resistance.getTimestamp() < 5000 && !rtConfig->resistance.getSimulate()) { rtConfig->setMinResistance(MIN_PELOTON_RESISTANCE); rtConfig->setMaxResistance(MAX_PELOTON_RESISTANCE); + rtConfig->resistance.setMin(MIN_PELOTON_RESISTANCE); + rtConfig->resistance.setMax(MAX_PELOTON_RESISTANCE); return true; } else { rtConfig->setMinResistance(-DEFAULT_RESISTANCE_RANGE);