From 5d14af6e6c080f2dcfe4844ceee95a5f9a849050 Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 1 Jan 2026 11:58:41 -0500 Subject: [PATCH 1/6] Fix G25 sequential shift direction Per comments in #21, this fixes the Logitech G25 shifter's sequential shifting so that upshifting is towards the user and downshifting is away from the user, matching the markings on the shifter itself. This changes the expected behavior of the up / down shift functions, but should maintain compatibility with existing user calibrations. --- src/SimRacing.cpp | 55 +++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/SimRacing.cpp b/src/SimRacing.cpp index e0261ae..861715d 100644 --- a/src/SimRacing.cpp +++ b/src/SimRacing.cpp @@ -1421,7 +1421,7 @@ LogitechShifterG25::LogitechShifterG25( { // using the calibration values from my own G25 shifter this->setCalibration({ 508, 435 }, { 310, 843 }, { 303, 8 }, { 516, 827 }, { 540, 14 }, { 713, 846 }, { 704, 17 }); - this->setCalibrationSequential(425, 619, 257); + this->setCalibrationSequential(425, 257, 619); } void LogitechShifterG25::begin() { @@ -1463,17 +1463,17 @@ bool LogitechShifterG25::updateState(bool connected) { // if we're neutral, check for up/down shift if (this->sequentialState == 0) { - if (y >= this->seqCalibration.upTrigger) this->sequentialState = 1; - else if (y <= this->seqCalibration.downTrigger) this->sequentialState = -1; + if (y <= this->seqCalibration.upTrigger) this->sequentialState = 1; + else if (y >= this->seqCalibration.downTrigger) this->sequentialState = -1; } // if we're in up-shift mode, check for release - else if ((this->sequentialState == 1) && (y < this->seqCalibration.upRelease)) { + else if ((this->sequentialState == 1) && (y > this->seqCalibration.upRelease)) { this->sequentialState = 0; } // if we're in down-shift mode, check for release - else if ((this->sequentialState == -1) && (y > this->seqCalibration.downRelease)) { + else if ((this->sequentialState == -1) && (y < this->seqCalibration.downRelease)) { this->sequentialState = 0; } @@ -1526,16 +1526,35 @@ void LogitechShifterG25::setCalibrationSequential(int neutral, int up, int down, releasePoint = engagePoint; } + // if up/down calibration points are reversed, swap them + // + // in v2 of the library, pushing the shifter was 'shift up' and pulling the + // shifter was 'shift down' + // + // in v3 of the library this was fixed, so that pushing the shifter is + // 'shift down' and pulling the shifter is 'shift up', matching the + // markings on the shifter itself (or mine, at least). this also matches + // the behavior of a sequential shift lever in a real rally car + // + // by swapping the calibration points here, the function maintains + // compatibility with calibration lines written for both version of + // the library + if(up > down) { + int temp = up; + up = down; // dogs and cats living together, mass hysteria + down = temp; + } + // calculate ranges - const int upRange = up - neutral; - const int downRange = neutral - down; + const int upRange = neutral - up; + const int downRange = down - neutral; // calculate calibration points - this->seqCalibration.upTrigger = neutral + (upRange * engagePoint); - this->seqCalibration.upRelease = neutral + (upRange * releasePoint); + this->seqCalibration.upTrigger = neutral - (upRange * engagePoint); + this->seqCalibration.upRelease = neutral - (upRange * releasePoint); - this->seqCalibration.downTrigger = neutral - (downRange * engagePoint); - this->seqCalibration.downRelease = neutral - (downRange * releasePoint); + this->seqCalibration.downTrigger = neutral + (downRange * engagePoint); + this->seqCalibration.downRelease = neutral + (downRange * releasePoint); } void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { @@ -1582,14 +1601,14 @@ void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { const uint8_t NumPoints = 3; const char* directions[2][2] = { - { "away from you", "up" }, - { "towards you", "down" }, + { "towards you", "up" }, + { "away from you", "down" }, }; int data[NumPoints]; int& neutral = data[0]; - int& yMax = data[1]; - int& yMin = data[2]; + int& yMin = data[1]; + int& yMax = data[2]; for (uint8_t i = 0; i < NumPoints; ++i) { if (i == 0) { @@ -1635,7 +1654,7 @@ void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { flushClient(iface); // apply and print - this->setCalibrationSequential(neutral, yMax, yMin, engagementPoint, releasePoint); + this->setCalibrationSequential(neutral, yMin, yMax, engagementPoint, releasePoint); iface.println(F("Here is your calibration:")); iface.println(separator); @@ -1645,10 +1664,10 @@ void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { iface.print(neutral); iface.print(", "); - iface.print(yMax); - iface.print(", "); iface.print(yMin); iface.print(", "); + iface.print(yMax); + iface.print(", "); iface.print(engagementPoint); iface.print(", "); From b7cc4e315c52e5a04c664350a5c83d5a8d71a46b Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 1 Jan 2026 12:16:14 -0500 Subject: [PATCH 2/6] Print position in G25 sequential calibration User feedback, matching the H-pattern calibration --- src/SimRacing.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/SimRacing.cpp b/src/SimRacing.cpp index 861715d..ae60e65 100644 --- a/src/SimRacing.cpp +++ b/src/SimRacing.cpp @@ -1626,7 +1626,12 @@ void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { this->update(); data[i] = this->getPositionRaw(Axis::Y); - iface.println(); // spacing + + iface.print(F("Shifter position recorded as ")); + iface.print('\''); + iface.print(data[i]); + iface.print('\''); + iface.println('\n'); // spacing } iface.println(F("These settings are optional. Send 'y' to customize. Send any other character to continue with the default values.")); From ff768cde488d47768368bf9be7b99f8c8fb9008d Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 1 Jan 2026 12:27:04 -0500 Subject: [PATCH 3/6] Simplify G25 sequential calibration wording Same behavior, less wordy --- src/SimRacing.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SimRacing.cpp b/src/SimRacing.cpp index ae60e65..e1e2bf4 100644 --- a/src/SimRacing.cpp +++ b/src/SimRacing.cpp @@ -1601,8 +1601,8 @@ void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { const uint8_t NumPoints = 3; const char* directions[2][2] = { - { "towards you", "up" }, - { "away from you", "down" }, + { "pull", "towards you" }, + { "push", "away from you" }, }; int data[NumPoints]; @@ -1615,9 +1615,9 @@ void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { iface.print(F("Leave the gear shifter in neutral")); } else { - iface.print(F("Please move the gear shifter ")); + iface.print(F("Please ")); iface.print(directions[i - 1][0]); - iface.print(F(" to sequentially shift ")); + iface.print(F(" the gear shifter ")); iface.print(directions[i - 1][1]); iface.print(F(" and hold it there")); } From 1d13cfd67d3b3e17dc9384e34ee0ed1ceeb6b7e6 Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 1 Jan 2026 12:27:22 -0500 Subject: [PATCH 4/6] Remove excess space in calibration printout --- src/SimRacing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SimRacing.cpp b/src/SimRacing.cpp index e1e2bf4..8649637 100644 --- a/src/SimRacing.cpp +++ b/src/SimRacing.cpp @@ -1103,7 +1103,7 @@ void AnalogShifter::serialCalibration(Stream& iface) { iface.println(separator); iface.println(); - iface.print(F("shifter.setCalibration( ")); + iface.print(F("shifter.setCalibration(")); for (int i = 0; i < 7; i++) { iface.print('{'); @@ -1665,7 +1665,7 @@ void LogitechShifterG25::serialCalibrationSequential(Stream& iface) { iface.println(separator); iface.println(); - iface.print(F("shifter.setCalibrationSequential( ")); + iface.print(F("shifter.setCalibrationSequential(")); iface.print(neutral); iface.print(", "); From 167d1c612cc3f584b225c004fcb7b854c54068eb Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 1 Jan 2026 12:36:01 -0500 Subject: [PATCH 5/6] Clarify sequential shift position docs --- src/SimRacing.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/SimRacing.h b/src/SimRacing.h index fd5a780..11249e0 100644 --- a/src/SimRacing.h +++ b/src/SimRacing.h @@ -1154,18 +1154,24 @@ namespace SimRacing { bool inSequentialMode() const; /** - * Check if the sequential shifter is shifted up + * Check if the sequential shifter is in the shift up position + * + * When in sequential mode, the shifter is considered to be in the + * "shift up" position when it is pulled towards the user. * - * @returns 'true' if the sequential shifter is shifted up, - * 'false' otherwise + * @returns 'true' if the sequential shifter is in the shift up + * position, 'false' otherwise */ bool getShiftUp() const; /** - * Check if the sequential shifter is shifted down + * Check if the sequential shifter is in the shift down position * - * @returns 'true' if the sequential shifter is shifted down, - * 'false' otherwise + * When in sequential mode, the shifter is considered to be in the + * "shift down" position when it is pushed away from the user. + * + * @returns 'true' if the sequential shifter is in the shift down + * position, 'false' otherwise */ bool getShiftDown() const; From 274bf912c3901aa0c72d4f731e0cf3ea9bfd41f1 Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 1 Jan 2026 12:57:08 -0500 Subject: [PATCH 6/6] Reword G25 shifter sequential swap comment --- src/SimRacing.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/SimRacing.cpp b/src/SimRacing.cpp index 8649637..b56cd71 100644 --- a/src/SimRacing.cpp +++ b/src/SimRacing.cpp @@ -1528,17 +1528,16 @@ void LogitechShifterG25::setCalibrationSequential(int neutral, int up, int down, // if up/down calibration points are reversed, swap them // - // in v2 of the library, pushing the shifter was 'shift up' and pulling the - // shifter was 'shift down' + // in the original public release, pushing the shifter was 'shift up' + // and pulling the shifter was 'shift down' // - // in v3 of the library this was fixed, so that pushing the shifter is - // 'shift down' and pulling the shifter is 'shift up', matching the - // markings on the shifter itself (or mine, at least). this also matches - // the behavior of a sequential shift lever in a real rally car + // this bug was eventually fixed, so that now pushing the shifter is + // 'shift down' and pulling the shifter is 'shift up'. This matches the + // markings on the shifter itself (or mine, at least), and mirrors the + // behavior of a sequential shift lever in a real rally car. // // by swapping the calibration points here, the function maintains - // compatibility with calibration lines written for both version of - // the library + // compatibility with calibration lines written for both versions if(up > down) { int temp = up; up = down; // dogs and cats living together, mass hysteria