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 Bike+ compatibility? #73

Open
skoregon opened this issue Mar 8, 2021 · 10 comments
Open

Peloton Bike+ compatibility? #73

skoregon opened this issue Mar 8, 2021 · 10 comments

Comments

@skoregon
Copy link

skoregon commented Mar 8, 2021

I installed and used Gymnasticon on a friend's Peloton Bike. It was great. My wife is now in possession of a Peloton Bike+. Any chance of that being added to the project? I'm a useless dev, but happy to poke around with anything else if helpful.

@skoregon
Copy link
Author

skoregon commented Mar 8, 2021

https://fccid.io/2AA3N-TTR01
The FCC filing has some photos of the insides of Bike+
"Internal Photos 2" appears to show the board in the resistance control unit. "Internal Photos 1" is mostly the tablet.

@skoregon
Copy link
Author

skoregon commented Mar 8, 2021

It appears that this is the processor for the sensor/resistance board (based on the FCC photos of internals):
https://www.st.com/resource/en/datasheet/stm32l432kc.pdf
Controller for the resistance unit:
https://www.st.com/resource/en/datasheet/l9942.pdf

@ptx2
Copy link
Owner

ptx2 commented Mar 10, 2021

Hi @skoregon, nice investigating!

I don't have access to a Bike+ so take everything I say with a grain of salt. Also just a warning to anyone reading: there's always a chance of damaging gear, so please don't try anything below unless you are comfortable doing so.

I have heard that the connection between sensor board and tablet is USB with a Type-C connector on the tablet side? If so, I would guess based on that microcontroller datasheet you linked (STM32L432KC) that it is USB 2.0. And that it probably shows up as a CDC (Virtual Com Port) when plugged into a computer/tablet. If so, it should be possible to communicate with it the same way as the Peloton Bike (non-plus). It is even possible that parts of the protocol are the same and some things Just Work already. It is of course possible that they made an entirely new protocol too.

An easy first step would be to plug the USB cable into a computer, see if a USB CDC device shows up, try running gymnasticon with --peloton-path pointing at the USB CDC device path. You could also use a terminal program like screen or a simple pyserial / node-serialport script to play around with it.

If that actually works then I think the next things to figure out would be:

  • Resistance control messages
  • Simple wiring options for passive/active use-cases

If not, a next step could be to try capture some data and figure out the protocol.

@skoregon
Copy link
Author

I plugged in the USBC cable from the resistance control unit into a Pi4revB running gymnasticon. I add the line
dtoverlay=dwc2,dr_mode=host
to the config.txt file as I assume it is needed to put the USB C port into host mode so it can gather data from the resistance control unit.

The Pi4 boots when connected to the USB-C from the resistance control unit, so that's a good start. I got on the bike and pedaled a bit and it is in a no resistance state.

libusb-t yields
pi@gymnasticon:/ $ lsusb -t

/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Communications, Driver=cdc_acm, 12M
    |__ Port 1: Dev 2, If 1, Class=CDC Data, Driver=cdc_acm, 12M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M

dmesg output attached.

dmesgoutput.txt

I don't see anything that looks like a device, or the peloton resistance control unit attached. I'm a novice, so I'm not sure how to more thoroughly interrogate the Pi4's USB attachments, or even if I've configured it correctly. See link below for some discussion. It is unclear to me if the Pi4 is a suitable device for this project or not.
https://www.raspberrypi.org/forums/viewtopic.php?t=246348

Any further guidance would be welcome on how to investigate.

I'm also having a difficult time figuring out how gymnasticon would work from a cabling perspective. Somehow the data needs to pass through the pi and be manipulated, then passed back both to the resistance control unit (if resistance control can be characterized) as well as downstream to the tablet.

At the same time, power over the USBC cable has to be provided to the tablet and the RCU and presumably the Pi, although the Pi could possible do its data interface over the USB A ports with a A->C converter and be powered separately? If that strategy were used though, somehow the data needs to get back into the USB C cable w/ the power to for both the resistance control unit and the tablet.

@ptx2
Copy link
Owner

ptx2 commented Mar 20, 2021

Hey, nice progress!

I think this confirms some of our assumptions about the communications:

(from your dmesg)

[    4.892304] usb 3-1: New USB device found, idVendor=317e, idProduct=a005, bcdDevice= 2.00
[    4.892324] usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    4.892340] usb 3-1: Product: Peloton Titan
[    4.892356] usb 3-1: Manufacturer: Peloton
[    4.892370] usb 3-1: SerialNumber: 00000000001A
[    4.981078] cdc_acm 3-1:1.0: ttyACM0: USB ACM device
[    4.981703] usbcore: registered new interface driver cdc_acm
[    4.981716] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters

(and lsusb -t)

/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Communications, Driver=cdc_acm, 12M
        |__ Port 1: Dev 2, If 1, Class=CDC Data, Driver=cdc_acm, 12M

Do you see a /dev/ttyACM0 device when the bike is connected? If so, an easy thing to try is running gymnasticon with --bike peloton and --peloton-path /dev/ttyACM0. If no luck, then I suppose it's time to figure out the protocol :-)

Great points on the wiring...

For the non-plus bike there are two wiring options with Gymnasticon: active and passive. Active mode is when the tablet remains disconnected and Gymnasticon polls the bike for power/cadence/resistance data. Passive mode is when the tablet does the polling and Gymnasticon just listens to the responses on the wire. The user chooses the mode with their RS232 cable wiring.

I feel both active and passive modes would be useful on the Bike+ too. The wiring for active mode is hopefully just plugging the bike directly into the Pi as you've already done. The wiring for passive mode, as you point out, presents some challenges.

For development purposes maybe something like this could work:

separate power adapter -> tablet's "P" USB-C port (this is just to power the tablet)
bike -> computer (acting as both usb host and device/gadget) -> tablet's second USB-C port

The idea is that hopefully the tablet doesn't care which USB port the bike's serial device appears on.

One issue with this setup is I'm pretty sure you can't use a stock Raspberry Pi for the "computer". Don't quote me but I think none of them have the hardware to act as host and gadget at the same time. You may be able to add a second USB controller via SPI but I'm not sure of an easy off-the-shelf option. I think the BeagleBone Black might be able to do it without modification though.

A similar option using two computers:

separate power adapter -> tablet's "P" USB-C port (this is just to power the tablet)
bike -> computerA (usb host)
computerB (usb device/gadget) -> tablet's second USB-C port

(computerA and computerB talk over the network)

You could use the Pi for computerB and any computer with a USB port for computerA.

Once setup, and assuming it works, on Linux I think you should get a /dev/ttyGS0 when the gadget serial port is set up. And hopefully you already have /dev/ttyACM0 for the bike. From there a small glue program could be written to just forward data between /dev/ttyACM0 and /dev/ttyGS0 in both directions and log it for analysis. socat(1) could probably do this without writing any code. PySerial comes with some forwarding examples that work over the network too.

Some links that might help:

https://www.isticktoit.net/?p=1383
https://www.kernel.org/doc/html/latest/usb/gadget_configfs.html
https://www.kernel.org/doc/html/latest/usb/gadget_serial.html
https://www.kernel.org/doc/html/latest/usb/gadget_multi.html
https://github.com/usb-tools/Facedancer
https://beagleboard.org/black

Also I found this one useful for pictures and background since I don't have the bike:

https://www.dcrainmaker.com/2020/09/peloton-bike-plus-details-features.html

Let me know if I can help, good luck!

EDIT: BTW, when setting up the CDC gadget on Linux, it'd be a good idea to use the same device info as the bike, i.e. idVendor, idProduct, Manufacturer, etc. so it looks right to the tablet.

@ChrisJefferson
Copy link

I was looking at this as well -- just in case anyone finds this useful, here is a windows USB dump. I think it will be better to try to do actual investigation via Linux.

   ---===>Device Information<===---

English product name: "Peloton Titan"

ConnectionStatus:
Current Config Value: 0x01 -> Device Bus Speed: Full (is not SuperSpeed or higher capable)
Device Address: 0x05
Open Pipes: 3

      ===>Device Descriptor<===

bLength: 0x12
bDescriptorType: 0x01
bcdUSB: 0x0201
bDeviceClass: 0x02 -> This is a Communication Device
bDeviceSubClass: 0x02
*!*ERROR: bDeviceSubClass of 2 is invalid
bDeviceProtocol: 0x00
bMaxPacketSize0: 0x40 = (64) Bytes
idVendor: 0x317E = Vendor ID not listed with USB.org
idProduct: 0xA005
bcdDevice: 0x0200
iManufacturer: 0x01
English (United States) "Peloton"
iProduct: 0x02
English (United States) "Peloton Titan"
iSerialNumber: 0x03
English (United States) "00000000001A"
bNumConfigurations: 0x01

      ---===>Open Pipes<===---

      ===>Endpoint Descriptor<===

bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0008 = 0x08 bytes
bInterval: 0x10

      ===>Endpoint Descriptor<===

bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x01 -> Direction: OUT - EndpointID: 1
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00

      ===>Endpoint Descriptor<===

bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x81 -> Direction: IN - EndpointID: 1
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00

   ---===>Full Configuration Descriptor<===---

      ===>Configuration Descriptor<===

bLength: 0x09
bDescriptorType: 0x02
wTotalLength: 0x0043 -> Validated
bNumInterfaces: 0x02
bConfigurationValue: 0x01
iConfiguration: 0x00
bmAttributes: 0xC0 -> Self Powered
MaxPower: 0x32 = 100 mA

      ===>Interface Descriptor<===

bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x00
bAlternateSetting: 0x00
bNumEndpoints: 0x01
bInterfaceClass: 0x02 -> This is Communications (CDC Control) USB Device Interface Class
bInterfaceSubClass: 0x02
bInterfaceProtocol: 0x01
iInterface: 0x00
-> This is a Communications (CDC Control) USB Device Interface Class

      ===>Descriptor Hex Dump<===

bLength: 0x05
bDescriptorType: 0x24
05 24 00 10 01
-> This is a Communications (CDC Control) USB Device Interface Class

      ===>Descriptor Hex Dump<===

bLength: 0x05
bDescriptorType: 0x24
05 24 01 00 01
-> This is a Communications (CDC Control) USB Device Interface Class

      ===>Descriptor Hex Dump<===

bLength: 0x04
bDescriptorType: 0x24
04 24 02 02
-> This is a Communications (CDC Control) USB Device Interface Class

      ===>Descriptor Hex Dump<===

bLength: 0x05
bDescriptorType: 0x24c
05 24 06 00 01

      ===>Endpoint Descriptor<===

bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0008 = 0x08 bytes
bInterval: 0x10

      ===>Interface Descriptor<===

bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x01
bAlternateSetting: 0x00
bNumEndpoints: 0x02
bInterfaceClass: 0x0A -> This is a CDC Data USB Device Interface Class
bInterfaceSubClass: 0x00
bInterfaceProtocol: 0x00
iInterface: 0x00

      ===>Endpoint Descriptor<===

bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x01 -> Direction: OUT - EndpointID: 1
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00

      ===>Endpoint Descriptor<===

bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x81 -> Direction: IN - EndpointID: 1
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00

      ===>BOS Descriptor<===

bLength: 0x05
bDescriptorType: 0x0F
wTotalLength: 0x000C
bNumDeviceCaps: 0x01

      ===>USB 2.0 Extension Descriptor<===

bLength: 0x07
bDescriptorType: 0x10
bDevCapabilityType: 0x02
bmAttributes: 0x00000002 -> Supports Link Power Management protocol

@antgonza
Copy link

antgonza commented Jan 5, 2023

Hello, just checking if there has been any progress with this issue (adding support for Peloton Bike+). BTW I have a Peloton Bike+ and I'm waiting for the hardware to start testing and could help with this.

@kasselvania
Copy link

Hi, I’m assuming folks probably dropped this project, but if not would someone be willing to update how it’s going and if the bike+ ever was fully attempted? Thanks!

@doudar
Copy link

doudar commented Dec 23, 2024

Hi, I’m assuming folks probably dropped this project, but if not would someone be willing to update how it’s going and if the bike+ ever was fully attempted? Thanks!

I haven't seen much action here, but in doudar/SmartSpin2k , there's the possibility to use a pre-existing power table (lookup table), combined with a cadence sensor and existing knob homing to achieve this. It's not there yet, but all the pieces of the puzzle are in place that it's within reach. It's on my timeline, but if someone else wanted to attempt it, it would probably get done much sooner.

@kasselvania
Copy link

@doudar I’ll come by the SmartSpin2k space and see if possibly I may be able to jump in. I don’t have the smartspin, mainly because I had seen that there wasn’t bike+ compatibility at the time.

Maybe we can find a way to coordinate and at the very least I could run code for you, test some things, or provide data.

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

No branches or pull requests

6 participants