This is a work-in-progress dump of notes. A full post with attributions will come later.

All references to code are from decompiling the nautilus_utility_backup.apk from this comment by Aktif8 on /r/Bowflex

There are 2 sensors on the bike

  • Resistance sensor (where the flywheel meets the resistance caliper)
  • Speed sensor (where the flywheel meets the Y-bar)

There is a board on the inside of the bike frame that connects to these sensors (I’m calling this the sensor board).

The sensor board connects directly to a daughterboard in the console on socket J9. Based on the wiring diagram Sebastian Barrenechea found for his treadmill, this looks like the UCB (universal controller board). Based on other ports on this board and hints from the code, Bowflex likely uses this same board for many different treads/bikes/etc.

Sensor board - to resistance/tach (top), to console (bottom) sensor board

Back of the C7 console - UCB J9 socket highlighted in red c7 console

The 12v from the bike’s power adapter goes directly to the console (left side of branch coming from bike). That J9 socket is then powering the sensor board with 3.3v. This means we should be able to power the sensor with 3.3v (like from a pi) without needing the console.

I’ve been able to read data coming back from the sensor board with a logic analyzer (230400 baud, RX inverted)

This is what a single packet looks like pulseview screencap

Dump of an entire capture session (~2 packets/second for ~19 seconds total).

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
                                |COUNT|                 |-- -- -- -- -- -- -- --|
3C 30 39 30 30 30 32 33 33 30 30 34 37 30 30 30 42 30 30 36 46 38 30 31 33 34 35 3B
3C 30 39 30 30 30 32 33 33 30 30 34 38 30 30 30 42 30 30 33 39 39 30 37 30 31 44 3B
3C 30 39 30 30 30 32 33 33 30 30 34 39 30 30 30 42 30 30 35 43 46 37 43 43 41 35 3B
3C 30 39 30 30 30 32 33 33 30 30 34 41 30 30 30 42 30 30 42 32 35 38 37 39 42 37 3B
3C 30 39 30 30 30 32 33 33 30 30 34 42 30 30 30 42 30 30 44 37 33 46 43 35 30 46 3B
3C 30 39 30 30 30 32 33 33 30 30 34 43 30 30 30 42 30 30 36 45 30 37 31 32 39 32 3B
3C 30 39 30 30 30 32 33 33 30 30 34 44 30 30 30 42 30 30 30 42 36 30 41 45 32 41 3B
3C 30 39 30 30 30 32 33 33 30 30 34 45 30 30 30 42 30 30 45 35 43 46 31 42 33 38 3B
3C 30 39 30 30 30 32 33 33 30 30 34 46 30 30 30 42 30 30 38 30 41 38 41 37 38 30 3B
3C 30 39 30 30 30 32 33 33 30 30 35 30 30 30 30 42 30 30 34 39 45 46 44 44 38 38 3B
3C 30 39 30 30 30 32 33 33 30 30 35 31 30 30 30 42 30 30 32 43 38 38 36 31 33 30 3B
3C 30 39 30 30 30 32 33 33 30 30 35 32 30 30 30 42 30 30 43 32 32 37 44 34 32 32 3B
3C 30 39 30 30 30 32 33 33 30 30 35 33 30 30 30 42 30 30 41 37 34 30 36 38 39 41 3B
3C 30 39 30 30 30 32 33 33 30 30 35 34 30 30 30 42 30 30 31 45 37 38 42 46 30 37 3B
3C 30 39 30 30 30 32 33 33 30 30 35 35 30 30 30 42 30 30 37 42 31 46 30 33 42 46 3B
3C 30 39 30 30 30 32 33 33 30 30 35 36 30 30 30 42 30 30 39 35 42 30 42 36 41 44 3B
3C 30 39 30 30 30 32 33 33 30 30 35 37 30 30 30 42 30 30 46 30 44 37 30 41 31 35 3B
3C 30 39 30 30 30 32 33 33 30 30 35 38 30 30 30 42 30 30 41 36 43 37 36 39 34 44 3B
3C 30 39 30 30 30 32 33 33 30 30 35 39 30 30 30 42 30 30 43 33 41 30 44 35 46 35 3B
3C 30 39 30 30 30 32 33 33 30 30 35 41 30 30 30 42 30 30 32 44 30 46 36 30 45 37 3B
3C 30 39 30 30 30 32 33 33 30 30 35 42 30 30 30 42 30 30 34 38 36 38 44 43 35 46 3B
3C 30 39 30 30 30 32 33 33 30 30 35 43 30 30 30 42 30 30 46 31 35 30 30 42 43 32 3B
3C 30 39 30 30 30 32 33 33 30 30 35 44 30 30 30 42 30 30 39 34 33 37 42 37 37 41 3B
3C 30 39 30 30 30 32 33 33 30 30 35 45 30 30 30 42 30 30 37 41 39 38 30 32 36 38 3B
3C 30 39 30 30 30 32 33 33 30 30 35 46 30 30 30 42 30 30 31 46 46 46 42 45 44 30 3B
3C 30 39 30 30 30 32 33 33 30 30 36 30 30 30 30 42 30 30 45 38 31 37 46 36 37 38 3B
3C 30 39 30 30 30 32 33 33 30 30 36 31 30 30 30 42 30 30 38 44 37 30 34 41 43 30 3B
3C 30 39 30 30 30 32 33 33 30 30 36 32 30 30 30 42 30 30 36 33 44 46 46 46 44 32 3B
3C 30 39 30 30 30 32 33 33 30 30 36 33 30 30 30 42 30 30 30 36 42 38 34 33 36 41 3B
3C 30 39 30 30 30 32 33 33 30 30 36 34 30 30 30 42 30 30 42 46 38 30 39 34 46 37 3B
3C 30 39 30 30 30 32 33 33 30 30 36 35 30 30 30 42 30 30 44 41 45 37 32 38 34 46 3B
3C 30 39 30 30 30 32 33 33 30 30 36 36 30 30 30 42 30 30 33 34 34 38 39 44 35 44 3B
3C 30 39 30 30 30 32 33 33 30 30 36 37 30 30 30 42 30 30 35 31 32 46 32 31 45 35 3B
3C 30 39 30 30 30 32 33 33 30 30 36 38 30 30 30 42 30 30 30 37 33 46 34 32 42 44 3B
3C 30 39 30 30 30 32 33 33 30 30 36 39 30 30 30 42 30 30 36 32 35 38 46 45 30 35 3B
3C 30 39 30 30 30 32 33 33 30 30 36 41 30 30 30 42 30 30 38 43 46 37 34 42 31 37 3B
3C 30 39 30 30 30 32 33 33 30 30 36 42 30 30 30 42 30 30 45 39 39 30 46 37 41 46 3B
3C 30 39 30 30 30 32 33 33 30 30 36 43 30 30 30 42 30 30 35 30 41 38 32 30 33 32 3B 

Some observations:

  • Data coming back on both D0 and D5 are identical, which is interesting.

  • D1 seems to mostly be a leading edge signal for D0/D5

  • D2 seems random

  • Any given packet from the bike looks like this:

    hex:

    3C 30 39 30 30 30 32 33 33 30 30 34 39 30 30 30 42 30 30 35 43 46 37 43 43 41 35 3B

    Converted to ascii:

    <090002330049000B005CF7CCA5;

    The hex (excluding the first and last byte) only has values between 0x30 and 0x46. More specifically, only values between 0x30-0x39 and 0x41-0x46.

    hex to ascii chart

    When converted to ASCII, that’s 0-9 and A-F… hex again. The ASCII representation of a hex string is being incoded in hex.

    There’s reference to this (decimal form) in com/nautilus/einstein/Util/BytesUtil.java:17

    private static final byte[] hexArray = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70};
    
  • Bytes 12 and 13 in the packet are a repeating hex sequence in this same range. (I’m guessing to validate packets are coming in the right order?)

Those aside, bytes 20-27 are the only other bytes that change between packets. Using the logic in com/nautilus packages, I haven’t been able to make any connection between them and the state of the bike (resistance, speed).

I’m beginning to suspect that the logic from com/ftdi/j2xx/ft4222/ is what takes in the raw data from the sensor board, does some sort of processing to it, and passes it to the app, but I’m not sure.

More to come…