Add native Topiom FS-442900 FitShow rower support - #4919
Conversation
|
Hardware test report — Topiom FS-442900 (thanks for the fast turnaround on this! 🚣) Tested the Working ✅ (Android, Pixel/Galaxy)
Bug 1 — 500m pace/split always 0:00Expected, given Bug 2 — Windows build connects then immediately drops (no data)On Windows (msvc2019, Qt5/WinRT) QZ discovers Proposed patch for Bug 1 (Speed / 500m split)Derive instantaneous Speed (km/h) from Δdistance/Δtime across consecutive 0x43 cumulative frames (both fields already decoded & verified — they drive the odometer). Guards against session resets and same-second polls. QZ then produces the 500m split from Speed automatically. --- a/src/devices/fitshowrower/fitshowrower.h
+++ b/src/devices/fitshowrower/fitshowrower.h
@@ class fitshowrower : public rower {
QDateTime lastStroke = QDateTime::currentDateTime();
+ // Track previous cumulative (0x43) sample to derive Speed from distance/time deltas.
+ qint32 lastCumulativeElapsed = -1;
+ quint16 lastCumulativeDistance = 0;
};
--- a/src/devices/fitshowrower/fitshowrower.cpp
+++ b/src/devices/fitshowrower/fitshowrower.cpp
@@ void fitshowrower::applyPacket(const Packet &packet) {
if (packet.command == 0x42) {
const bool running = packet.status == 0x02;
Cadence = running ? packet.cadence : 0;
m_watt = running ? packet.power : 0;
- Speed = 0; // The Topiom speed field has no verified unit; distance comes from command 0x43.
+ // Speed is derived from the verified cumulative distance/time deltas in the 0x43
+ // frame (below), NOT the unverified 0x42 speed bytes. Only clear it when idle so
+ // the 500m pace tile stays live while rowing.
+ if (!running)
+ Speed = 0;
@@ } else if (packet.command == 0x43) {
if (packet.strokeCount > StrokesCount.value()) {
lastStroke = QDateTime::currentDateTime();
}
+ // Instantaneous speed (km/h) from change in cumulative distance over change in
+ // elapsed time. Guards session resets (values going backwards) and same-second
+ // polls (deltaTime == 0 -> keep previous Speed).
+ if (lastCumulativeElapsed >= 0 &&
+ packet.elapsedSeconds >= static_cast<quint16>(lastCumulativeElapsed) &&
+ packet.distanceMeters >= lastCumulativeDistance) {
+ const int deltaTime = packet.elapsedSeconds - lastCumulativeElapsed;
+ const int deltaDistance = packet.distanceMeters - lastCumulativeDistance;
+ if (deltaTime > 0)
+ Speed = (static_cast<double>(deltaDistance) / deltaTime) * 3.6;
+ }
+ lastCumulativeElapsed = packet.elapsedSeconds;
+ lastCumulativeDistance = packet.distanceMeters;
elapsed = packet.elapsedSeconds;
Distance = packet.distanceMeters / 1000.0;
KCal = packet.calories;
StrokesCount = packet.strokeCount;Note: distance is integer meters polled ~1 s apart, so the split has ~±0.5 m/s quantization (a few seconds of jitter). A 3-sample moving average on Speed would smooth it if preferred, but the raw delta already gives a correct, usable 500m split. Happy to capture a debug log of a steady-state row if useful. |
|
Hi @maverickgmt i don't like the speed derived from distance, I would prefer to check in a log if it sends it directly. For the windows build use the msvc one instead Let me know |
Motivation
Description
fitshowrowerclass (src/devices/fitshowrower/fitshowrower.hand.../fitshowrower.cpp) derived fromrowerthat implements FitShow protocol parsing, checksum validation, lifecycle, and virtual device integration.0x41), live status (0x42), cumulative workout (0x43), and start/stop (0x44) messages, including distance high-bit scaling and power/cadence decoding.FFF0, subscribes to notifications onFFF1, and writes polls/commands toFFF2(using WriteWithoutResponse when available), performs the captured initialization sequence, and polls at ~2 Hz alternating live-status and cumulative-data without blocking for one-to-one replies.FS-442900(case-insensitive) and inserted before the genericFS-*/FitPlus handlers insrc/devices/bluetooth.cpp, and the new device is registered insrc/qdomyos-zwift.priandsrc/devices/bluetooth.h.maxResistance()==0), maps cadence->SPM, cumulative0x43distance->Distance (km),StrokesCount,KCal,elapsed, and power (from0x42), clears stale cadence/power when idle, and logs malformed frames and diagnostic information.tst/Devices/TestFitshowRowerParser.{h,cpp}covering capability packets, live-running and idle examples, cumulative distance scaling, checksum rejection, and detection narrowing, and updated test project include lists.Testing
git diff --checkand repository local checks which reported no whitespace/patch errors and passed.python3script to verify XOR checksums for the exact captured packets used in tests, which matched the expected FCS values.qmake/development toolchain) were not available.qmake && make) and exercise with a real Topiom FS-442900 device to validate end-to-end BLE behavior and virtual-device interactions.Codex Task