Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Peloton Direct Polling support #43

Merged
merged 9 commits into from
Feb 12, 2021

Conversation

jeremydk
Copy link
Contributor

@jeremydk jeremydk commented Feb 9, 2021

Based on the work in #24 (@vodlogic whom I can't add as a co-author, maybe @ptx2 can work some git magic), I've cleaned up the polling logic. This is working on my bike, but does, as we've previously observed, block the communication between the Peloton Tablet and the Bike once the serial Tx is connected.

This still requires a "crossover coupler", but will allow individuals who want to entirely bypass the (or have broken their) Peloton Tablet to get stats off their bike.

Addresses #22

const PACKET_DELIMITER = Buffer.from('f6', 'hex');
const POLL_RATE = 250;
Copy link
Contributor

@vodlogic vodlogic Feb 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you observed any issues with the poll rate being exceeded? Is there any benefit in making this value a configurable cli argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might eventually be configurable -- I've only tested for a few minutes at the moment to validate, but I know that there have been concerns around drift and lockup over long periods. Left this in as a way to help debug/investigate that behavior if we get a repro. I suspect the serialPort.drain() actually covers the underlying cause, but will need data to show that.

debuglog(`Measurement receive trigger: ${this.receiveTrigger}`);
if (this.receiveTrigger === RECEIVE_TRIGGER.POLL) {
if ((Object.entries(MEASUREMENTS_HEX_ENUM).length * SERIAL_WRITE_TIMEOUT) > POLL_RATE) {
throw new Error("Max Serial Write Timeout is longer than Poll Rate.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this abort the gymnasticon process and one would need to restart it? Wondering if this should be just logged as a warning instead unless a one off timeout materially impacts the zwift experience?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, my expectation is that this is fatal. It's a subtle misconfiguration, but this keeps the peloton emitting on schedule. Without this check, assuming we were in a case where our serial writes were slow (near our timeout values), we could theoretically still be executing when we queue the next run of the poller. While node handles this gracefully (being single-threaded and all), it causes a delay between stats being emitted to Gymnasticon. If this keeps occurring, it's possible for us to fall behind, and given that it's a very simple check, I figured this was safer.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove the receiveTrigger option and just always send the poll requests? It seems that would work for both types of wiring. The main benefit would be UX: the user can just plug-in and ride with any wiring without editing cli/config. We could just default to polling mode, but I'm wondering if there's a use-case for the passive mode anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll need to test a few scenarios from a wiring front -- If I leave the serial Tx floating, it should be safe to always send the poll requests. The real grail for this would be allowing gymnasticon to poll the bike while still allowing for metrics to be received by the tablet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay -- Tested this with manual rewiring. I can toggle between having gymnasticon be the authoritative source, or the Peloton Tablet, and both are confirmed working with this latest commit. I'm sketching out some connectivity to trace -- I might draw up specs for a small project to give people a hardware toggle switch between the authoritative sources while maintaining stats on the Tablet itself for those interested.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@vodlogic
Copy link
Contributor

vodlogic commented Feb 9, 2021

Based on the work in #24 (@vodlogic whom I can't add as a co-author, maybe @ptx2 can work some git magic), I've cleaned up the polling logic. This is working on my bike, but does, as we've previously observed, block the communication between the Peloton Tablet and the Bike once the serial Tx is connected.

This still requires a "crossover coupler", but will allow individuals who want to entirely bypass the (or have broken their) Peloton Tablet to get stats off their bike.

Addresses #22

Thanks so much for continuing the work here, I really appreciate it!

@ptx2
Copy link
Owner

ptx2 commented Feb 11, 2021

@jeremydk thanks for picking this up, excited to add this feature!

@jeremydk
Copy link
Contributor Author

Here's a dump of 1s worth of responses.

Looking at the second value, we can determine the response type.
4a -> 74 -> Resistance
41 -> 65 -> Cadence
44 -> 68 -> Power

Looking at the timestamps from the parsing, it does look like you're correct, we're iterating through sending a request every 100ms.

I'll get this patched to replicate the behavior.

Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 41 03 37 35 30 d1>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: [2021-02-12T04:00:50.198Z] received stats from bike [power=12W cadence=57rpm]
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 44 05 36 30 31 30 30 31>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: [2021-02-12T04:00:50.300Z] received stats from bike [power=11W cadence=57rpm]
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 4a 04 30 33 32 30 04>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 41 03 38 35 30 d2>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: [2021-02-12T04:00:50.502Z] received stats from bike [power=11W cadence=58rpm]
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 44 05 31 31 31 30 30 2d>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: [2021-02-12T04:00:50.603Z] received stats from bike [power=11W cadence=58rpm]
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: [2021-02-12T04:00:50.663Z] pedal stroke [timestamp=1613102450661.558 revolutions=10 power=11W]
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 4a 04 36 32 32 30 09>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 41 03 39 35 30 d3>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: [2021-02-12T04:00:50.803Z] received stats from bike [power=11W cadence=59rpm]
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: GYMNASTICON:BIKES:PELOTON:TRACE 5357: RECV:  <Buffer f1 44 05 31 31 31 30 30 2d>
Feb 11 23:00:50 gymnasticon gymnasticon[5357]: [2021-02-12T04:00:50.904Z] received stats from bike [power=11W cadence=59rpm]

@jeremydk
Copy link
Contributor Author

Traces from the latest patch, metric requests driven from Gymnasticon directly.

Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 4a 04 33 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 41 03 30 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: [2021-02-12T05:16:03.165Z] received stats from bike [power=0W cade
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 44 05 30 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: [2021-02-12T05:16:03.273Z] received stats from bike [power=0W cade
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 4a 04 33 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 41 03 30 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: [2021-02-12T05:16:03.466Z] received stats from bike [power=0W cade
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 44 05 30 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: [2021-02-12T05:16:03.571Z] received stats from bike [power=0W cade
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 4a 04 33 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 41 03 30 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: [2021-02-12T05:16:03.767Z] received stats from bike [power=0W cade
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 44 05 30 3
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: [2021-02-12T05:16:03.872Z] received stats from bike [power=0W cade
Feb 12 00:16:03 gymnasticon gymnasticon[5762]: GYMNASTICON:BIKES:PELOTON:TRACE 5762: RECV:  <Buffer f1 4a 04 33 3

@jeremydk
Copy link
Contributor Author

jeremydk commented Feb 12, 2021 via email

@ptx2 ptx2 merged commit 40cd8ce into ptx2:master Feb 12, 2021
@jeremydk jeremydk deleted the jeremydk/peloton_direct_polling branch February 12, 2021 23:17
@ptx2
Copy link
Owner

ptx2 commented Feb 12, 2021

Looks good! Thanks for the work on this @jeremydk and @vodlogic. Will get a release out this weekend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants