Some additional information about the protocol for the Proform exercise bike.
Bytes that change the incline on the proform bike. All incline change commands have a common number of bytes. Only bytes 12-15 change.
Common command:
BA, B4, B1, B0, B6, B0, B0, B0, B1, B0, B0, Byte12, Byte13, Byte14, Byte15, 8D, 8A
Bytes that change the gear on the proform bike. All gear change commands have a common number of bytes. Only bytes 12-15 change.
Common command:
BA, B6, B1, B0, B6, B0, B0, B0, B5, B0, B0, Byte12, Byte13, Byte14, Byte15, 8D, 8A
There seem to be some gaps between bytes. In fact, we can fill in the pattern and end up with approximately 60 different gears/resistance levels. I feel that the upper end of the gears could be extended too, although I haven't tried to go beyond the measured pulses.
Here are the filled in gear codes for bytes 12-15.
Byte12 Byte13 Byte14 Byte15 Gear
B0 C6 B8 B5 1
B1 B0 B8 B4 1.5
B1 B1 B8 B3 2
B1 B2 B8 B2 2.3
B1 B3 B8 B1 2.6
B1 B4 B8 B0 3
B1 B5 B7 C6 3.3
B1 B6 B7 C5 3.6
B1 B7 B7 C4 4
B1 B8 B7 C3 4.3
B1 B9 B7 C2 4.6
B1 C1 B7 C1 5
B1 C2 B7 B9 5.3
B1 C3 B7 B8 5.6
B1 C4 B7 B7 6
B1 C5 B7 B6 6.3
B1 C6 B7 B5 6.6
B2 B0 B7 B4 7
B2 B1 B7 B3 7.3
B2 B2 B7 B2 7.6
B2 B3 B7 B1 8
B2 B3 B7 B0 8.3
B2 B4 B6 C6 8.6
B2 B6 B6 C5 9
B2 B7 B6 C4 9.5
B2 B8 B6 C3 10
B2 B9 B6 C2 10.3
B2 C1 B6 C1 10.6
B2 C2 B6 B9 11
B2 C3 B6 B8 11.3
B2 C4 B6 B7 11.6
B2 C5 B6 B6 12
B2 C6 B6 B5 12.3
B3 B0 B6 B4 12.6
B3 B1 B6 B3 13
B3 B2 B6 B2 13.3
B3 B3 B6 B1 13.6
B3 B4 B6 B0 14
B3 B5 B5 C6 14.3
B3 B6 B5 C5 14.6
B3 B7 B5 C4 15
B3 B8 B5 C3 15.3
B3 B9 B5 C2 15.6
B3 C1 B5 C1 16
B3 C2 B5 B9 16.3
B3 C3 B5 B8 16.6
B3 C4 B5 B7 17
B3 C5 B5 B6 17.5
B3 C6 B5 B5 18
B4 B0 B5 B4 18.3
B4 B1 B5 B3 18.6
B4 B2 B5 B2 19
B4 B3 B5 B1 19.3
B4 B4 B5 B0 19.6
B4 B5 B4 C6 20
B4 B6 B4 C5 20.3
B4 B7 B4 C4 20.6
B4 B8 B4 C3 21
B4 B9 B4 C2 21.3
B4 C1 B4 C1 21.6
B4 C2 B4 B9 22
Here is a Fritzing schematic of the Arduino circuit I made to control the bike.
View attachment 277452
And the code for the Arduino:
Bytes that change the incline on the proform bike. All incline change commands have a common number of bytes. Only bytes 12-15 change.
Common command:
BA, B4, B1, B0, B6, B0, B0, B0, B1, B0, B0, Byte12, Byte13, Byte14, Byte15, 8D, 8A
Byte12 | Byte13 | Byte14 | Byte15 | Absolute Incline |
---|---|---|---|---|
B0 | B0 | C2 | B8 | -15 |
B0 | B1 | C2 | B7 | -14.5 |
B0 | B2 | C2 | B6 | -14 |
B0 | B3 | C2 | B5 | -13.5 |
B0 | B4 | C2 | B4 | -13 |
B0 | B5 | C2 | B3 | -12.5 |
B0 | B6 | C2 | B2 | -12 |
B0 | B7 | C2 | B1 | -11.5 |
B0 | B8 | C2 | B0 | -11 |
B0 | B9 | C1 | C6 | -10.5 |
B0 | C1 | C1 | C5 | -10 |
B0 | C2 | C1 | C4 | -9.5 |
B0 | C3 | C1 | C3 | -9 |
B0 | C4 | C1 | C2 | -8.5 |
B0 | C5 | C1 | C1 | -8 |
B0 | C6 | C1 | B9 | -7.5 |
B1 | B0 | C1 | B8 | -7 |
B1 | B1 | C1 | B7 | -6.5 |
B1 | B2 | C1 | B6 | -6 |
B1 | B3 | C1 | B5 | -5.5 |
B1 | B4 | C1 | B4 | -5 |
B1 | B5 | C1 | B3 | -4.5 |
B1 | B6 | C1 | B2 | -4 |
B1 | B7 | C1 | B1 | -3.5 |
B1 | B8 | C1 | B0 | -3 |
B1 | B9 | B9 | C6 | -2.5 |
B1 | C1 | B9 | C5 | -2 |
B1 | C2 | B9 | C4 | -1.5 |
B1 | C3 | B9 | C3 | -1 |
B1 | C4 | B9 | C2 | -0.5 |
B1 | C5 | B9 | C1 | ZERO LEVEL |
B1 | C6 | B9 | B9 | 0.5 |
B2 | B0 | B9 | B8 | 1 |
B2 | B1 | B9 | B7 | 1.5 |
B2 | B2 | B9 | B6 | 2 |
B2 | B3 | B9 | B5 | 2.5 |
B2 | B4 | B9 | B4 | 3 |
B2 | B5 | B9 | B3 | 3.5 |
B2 | B6 | B9 | B2 | 4 |
B2 | B7 | B9 | B1 | 4.5 |
B2 | B8 | B9 | B0 | 5 |
B2 | B9 | B8 | C6 | 5.5 |
B2 | C1 | B8 | C5 | 6 |
B2 | C2 | B8 | C4 | 6.5 |
B2 | C3 | B8 | C3 | 7 |
B2 | C4 | B8 | C2 | 7.5 |
B2 | C5 | B8 | C1 | 8 |
B2 | C6 | B8 | B9 | 8.5 |
B3 | B0 | B8 | B8 | 9 |
B3 | B1 | B8 | B7 | 9.5 |
B3 | B2 | B8 | B6 | 10 |
B3 | B3 | B8 | B5 | 10.5 |
B3 | B4 | B8 | B4 | 11 |
B3 | B5 | B8 | B3 | 11.5 |
B3 | B6 | B8 | B2 | 12 |
B4 | B7 | B8 | B1 | 12.5 |
B5 | B8 | B8 | B0 | 13 |
B6 | B9 | B8 | C6 | 13.5 |
B7 | C1 | B7 | C5 | 14 |
B8 | C2 | B7 | C4 | 14.5 |
B3 | C3 | B7 | C3 | 15 |
Bytes that change the gear on the proform bike. All gear change commands have a common number of bytes. Only bytes 12-15 change.
Common command:
BA, B6, B1, B0, B6, B0, B0, B0, B5, B0, B0, Byte12, Byte13, Byte14, Byte15, 8D, 8A
Byte12 | Byte13 | Byte14 | Byte15 | Gear |
B0 | C6 | B8 | B5 | 1 |
B1 | B1 | B8 | B3 | 2 |
B1 | B4 | B8 | B0 | 3 |
B1 | B7 | B7 | C4 | 4 |
B1 | C1 | B7 | C1 | 5 |
B1 | C4 | B7 | B7 | 6 |
B2 | B0 | B7 | B4 | 7 |
B2 | B3 | B7 | B1 | 8 |
B2 | B6 | B6 | C5 | 9 |
B2 | B8 | B6 | C3 | 10 |
B2 | C2 | B6 | B9 | 11 |
B2 | C5 | B6 | B6 | 12 |
B3 | B1 | B6 | B3 | 13 |
B3 | B4 | B6 | B0 | 14 |
B3 | B7 | B5 | C4 | 15 |
B3 | C1 | B5 | C1 | 16 |
B3 | C4 | B5 | B7 | 17 |
B3 | C6 | B5 | B5 | 18 |
B4 | B2 | B5 | B2 | 19 |
B4 | B5 | B4 | C6 | 20 |
B4 | B8 | B4 | C3 | 21 |
B4 | C2 | B4 | B9 | 22 |
There seem to be some gaps between bytes. In fact, we can fill in the pattern and end up with approximately 60 different gears/resistance levels. I feel that the upper end of the gears could be extended too, although I haven't tried to go beyond the measured pulses.
Here are the filled in gear codes for bytes 12-15.
Byte12 Byte13 Byte14 Byte15 Gear
B0 C6 B8 B5 1
B1 B0 B8 B4 1.5
B1 B1 B8 B3 2
B1 B2 B8 B2 2.3
B1 B3 B8 B1 2.6
B1 B4 B8 B0 3
B1 B5 B7 C6 3.3
B1 B6 B7 C5 3.6
B1 B7 B7 C4 4
B1 B8 B7 C3 4.3
B1 B9 B7 C2 4.6
B1 C1 B7 C1 5
B1 C2 B7 B9 5.3
B1 C3 B7 B8 5.6
B1 C4 B7 B7 6
B1 C5 B7 B6 6.3
B1 C6 B7 B5 6.6
B2 B0 B7 B4 7
B2 B1 B7 B3 7.3
B2 B2 B7 B2 7.6
B2 B3 B7 B1 8
B2 B3 B7 B0 8.3
B2 B4 B6 C6 8.6
B2 B6 B6 C5 9
B2 B7 B6 C4 9.5
B2 B8 B6 C3 10
B2 B9 B6 C2 10.3
B2 C1 B6 C1 10.6
B2 C2 B6 B9 11
B2 C3 B6 B8 11.3
B2 C4 B6 B7 11.6
B2 C5 B6 B6 12
B2 C6 B6 B5 12.3
B3 B0 B6 B4 12.6
B3 B1 B6 B3 13
B3 B2 B6 B2 13.3
B3 B3 B6 B1 13.6
B3 B4 B6 B0 14
B3 B5 B5 C6 14.3
B3 B6 B5 C5 14.6
B3 B7 B5 C4 15
B3 B8 B5 C3 15.3
B3 B9 B5 C2 15.6
B3 C1 B5 C1 16
B3 C2 B5 B9 16.3
B3 C3 B5 B8 16.6
B3 C4 B5 B7 17
B3 C5 B5 B6 17.5
B3 C6 B5 B5 18
B4 B0 B5 B4 18.3
B4 B1 B5 B3 18.6
B4 B2 B5 B2 19
B4 B3 B5 B1 19.3
B4 B4 B5 B0 19.6
B4 B5 B4 C6 20
B4 B6 B4 C5 20.3
B4 B7 B4 C4 20.6
B4 B8 B4 C3 21
B4 B9 B4 C2 21.3
B4 C1 B4 C1 21.6
B4 C2 B4 B9 22
Here is a Fritzing schematic of the Arduino circuit I made to control the bike.
View attachment 277452
And the code for the Arduino:
Code:
int incline = 30; //(levels from -15 to +15 by half increments. Index for zero is 30)
int gear = 0;
//expanded gear ranges. Can the resistance go higher?
//is the resistance increase at higher incline due to mechanical adjustment or due to new codes sent.
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61
byte gearCommand1[] ={0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4};
byte gearCommand2[] ={0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2};
byte gearCommand3[] ={0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4};
byte gearCommand4[] ={0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xB9, 0xB8, 0xB7, 0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xB9, 0xB8, 0xB7, 0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xB9, 0xB8, 0xB7, 0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xB9};
/*
//these are the bytes for gear changes.
//bytes 12 to 15. There could be more levels in between these, note the pattern of bytes for incline
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22
byte gearCommand1[] ={0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4};
byte gearCommand2[] ={0xC6, 0xB1, 0xB4, 0xB7, 0xC1, 0xC4, 0xB0, 0xB3, 0xB6, 0xB8, 0xC2, 0xC5, 0xB1, 0xB4, 0xB7, 0xC1, 0xC4, 0xC6, 0xB2, 0xB5, 0xB8, 0xC2};
byte gearCommand3[] ={0xB8, 0xB8, 0xB8, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB4, 0xB4, 0xB4};
byte gearCommand4[] ={0xB5, 0xB3, 0xB0, 0xC4, 0xC1, 0xB7, 0xB4, 0xB1, 0xC5, 0xC3, 0xB9, 0xB6, 0xB3, 0xB0, 0xC4, 0xC1, 0xB7, 0xB5, 0xB2, 0xC6, 0xC3, 0xB9};
*/
//these are the bytes for incline changes
//note that "0" is number 31 below and changes are 0.5 degree increments on the bike
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61
byte inclineCommand1[] = {0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3};
byte inclineCommand2[] = {0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC1, 0xC2};
byte inclineCommand3[] = {0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB7, 0xB7, 0xB7};
byte inclineCommand4[] = {0xB9, 0xB8, 0xB7, 0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xB9, 0xB8, 0xB7, 0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xB9, 0xB8, 0xB7, 0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xB9, 0xB8, 0xB7, 0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1, 0xB0, 0xC6, 0xC5, 0xC4};
byte inclineDownButton = 10;
byte inclineUpButton = 11;
byte gearDownButton = 9;
byte gearUpButton =8;
void setup() {
// put your setup code here, to run once:
Serial.begin(38400);
pinMode(inclineDownButton, INPUT_PULLUP); //
pinMode(inclineUpButton, INPUT_PULLUP); //
pinMode(gearDownButton, INPUT_PULLUP); //
pinMode(gearUpButton, INPUT_PULLUP); //
}
void loop() {
bool gearDown = digitalRead(gearDownButton);
bool gearUp = digitalRead(gearUpButton);
bool inclineDown = digitalRead(inclineDownButton);
bool inclineUp = digitalRead(inclineUpButton);
if (gearDown==LOW){
gear = gear - 1;
if (gear < 0){ gear = 0;}
Serial.write(0xBA);
Serial.write(0xB6);
Serial.write(0xB1);
Serial.write(0xB0);
Serial.write(0xB6);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB5);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(gearCommand1[gear]);
Serial.write(gearCommand2[gear]);
Serial.write(gearCommand3[gear]);
Serial.write(gearCommand4[gear]);
Serial.write(0x8D);
Serial.write(0x8A);
delay(500);
}
if (gearUp==LOW){
gear = gear + 1;
if (gear > 21){ gear = 21;}
Serial.write(0xBA);
Serial.write(0xB6);
Serial.write(0xB1);
Serial.write(0xB0);
Serial.write(0xB6);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB5);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(gearCommand1[gear]);
Serial.write(gearCommand2[gear]);
Serial.write(gearCommand3[gear]);
Serial.write(gearCommand4[gear]);
Serial.write(0x8D);
Serial.write(0x8A);
delay(500);
}
if (inclineDown==LOW){
incline = incline - 1;
if (incline < 0){ incline = 0;}
Serial.write(0xBA);
Serial.write(0xB4);
Serial.write(0xB1);
Serial.write(0xB0);
Serial.write(0xB6);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB1);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(inclineCommand1[incline]);
Serial.write(inclineCommand2[incline]);
Serial.write(inclineCommand3[incline]);
Serial.write(inclineCommand4[incline]);
Serial.write(0x8D);
Serial.write(0x8A);
delay(500);
}
if (inclineUp==LOW){
incline = incline + 1;
if (incline > 60){ incline = 60;} //max 60 entries in the incline array
Serial.write(0xBA);
Serial.write(0xB4);
Serial.write(0xB1);
Serial.write(0xB0);
Serial.write(0xB6);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(0xB1);
Serial.write(0xB0);
Serial.write(0xB0);
Serial.write(inclineCommand1[incline]);
Serial.write(inclineCommand2[incline]);
Serial.write(inclineCommand3[incline]);
Serial.write(inclineCommand4[incline]);
Serial.write(0x8D);
Serial.write(0x8A);
delay(500);
}
}