I scrapped a treadmill and salvaged the motor for a belt grinder project. My goal is to chuck the original control panel and just use the motor and it’s controller… but unlike the previous treadmill I did this to, interfacing with this one was not as simple as feeding it a PWM signal. The motor is a Johnson 90v DC motor, model JM01-013, and the control board is labeled MLH0910PC. I’m hoping the following information helps at least one other person – I spent hours searching and wasn’t able to come up with anything useful.
The control panel (I’ll refer to this as the panel going forward) has all of the displays and connects to the rest of the interface buttons and the safety key switch. The motor controller contains the AC connection, runs the motor and incline, as well as supplies power for the panel.
There is a single cable connecting the controller and panel with 8 wires: two are the safety key switch and pass straight through to the controller, two for power, two for ground, and the last two are for serial data. On both ends of the connector there is a MAX3085 RS-485 transceiver chip. Initially I used a MAX485 on a breakout board to listen in, but I was struggling to extract useful data with a logic analyzer and PulseView. It was clear that there was a message and response from one unit to the other, but for whatever reason I wasn’t able to reliably pick up the start and end bits for the response. I had much better luck connecting the logic analyzer directly to pins 1 and 4 of the MAX3085 (on either board works)… this let me see the RX and TX data on separate channels, then it could be decoded easily.
From what I can tell the panel is the master, it always initiates communication (either a command or a query), and the controller only responds. Messages are sent roughly every 70-100 ms at 9600 baud and consist of 5-7 bytes. The first two bytes look like an identifier: the panel prefix is 0-255, and the controller is 0-127. I looked at all of this in decimal because why not, also it made it easy to work with in Excel. I was able to isolate the packet from the panel that seemed to control the speed, then I recorded the values for every speed available from .5 to 12 MPH in tenth of a MPH increments.
I used an arduino to replay all of the messages through the MAX485 from power-up to running and was pleased to see the motor start to run (I kept the safety switch shorted). Working backwards, I started eliminating the different messages until all I had left was the speed command and the motor continued to run – showing me that I can ignore pretty much everything else – great! Did some more experimentation and here is what I found:
- Only speed commands are needed
- All acknowledgements from the controller can be ignored
- Any new speed lower than the current one will be accepted by the controller and it will coast down to that setting
- A new speed higher than the current one will be accepted if it is not too much higher. For example, the motor will accelerate from 5 to 5.1 mph
- A new speed too much higher than the current one will be rejected and the motor will coast to a stop. For example, commanding it to accelerate from 5 to 12 mph.
- Speed changes are very gradual (probably to keep you from falling on your face and/or butt)
- It won’t exceed 4000 RPM (12 mph), or perhaps I picked bogus values when I tried
This means that interfacing with this is going to be pretty easy. My approach was just to create a lookup table for the known speeds that I mapped out. When increasing the speed I ramp through each of the steps on the way to the final target speed, this seems to keep the controller happy and it doesn’t panic and coast to a stop. There is no need to ramp down to a slower speed, only to a higher one.
The 7-byte packet that controls speed is pictured above. I mapped all of the speeds from zero to twelve MPH, see the table below. RPM was measured with no load, but I expect it to be similar under load – the motor has a toothed wheel (maybe optical or probably hall-effect) and I’m guessing it uses closed-loop speed control.
Mission accomplished! I’ve actually had this motor sitting around for over a year but never made any headway on figuring out how to control it, now I can move on and make something useful with it.
MPH | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | Measured RPM |
---|---|---|---|---|---|---|---|---|
0.0 | 0 | 255 | 241 | 2 | 0 | 0 | 221 | 0 |
0.5 | 0 | 255 | 241 | 2 | 0 | 161 | 16 | |
0.6 | 0 | 255 | 241 | 2 | 0 | 195 | 201 | |
0.7 | 0 | 255 | 241 | 2 | 0 | 230 | 186 | |
0.8 | 0 | 255 | 241 | 2 | 1 | 8 | 144 | |
0.9 | 0 | 255 | 241 | 2 | 1 | 42 | 116 | |
1.0 | 0 | 255 | 241 | 2 | 1 | 76 | 105 | 332 |
1.1 | 0 | 255 | 241 | 2 | 1 | 111 | 188 | |
1.2 | 0 | 255 | 241 | 2 | 1 | 145 | 33 | |
1.3 | 0 | 255 | 241 | 2 | 1 | 179 | 197 | |
1.4 | 0 | 255 | 241 | 2 | 1 | 213 | 216 | |
1.5 | 0 | 255 | 241 | 2 | 1 | 248 | 18 | |
1.6 | 0 | 255 | 241 | 2 | 2 | 26 | 156 | |
1.7 | 0 | 255 | 241 | 2 | 2 | 60 | 188 | |
1.8 | 0 | 255 | 241 | 2 | 2 | 94 | 101 | |
1.9 | 0 | 255 | 241 | 2 | 2 | 129 | 79 | |
2.0 | 0 | 255 | 241 | 2 | 2 | 163 | 171 | 670 |
2.1 | 0 | 255 | 241 | 2 | 2 | 197 | 182 | |
2.2 | 0 | 255 | 241 | 2 | 2 | 231 | 82 | |
2.3 | 0 | 255 | 241 | 2 | 3 | 10 | 43 | |
2.4 | 0 | 255 | 241 | 2 | 3 | 44 | 11 | |
2.5 | 0 | 255 | 241 | 2 | 3 | 78 | 210 | |
2.6 | 0 | 255 | 241 | 2 | 3 | 112 | 8 | |
2.7 | 0 | 255 | 241 | 2 | 3 | 147 | 154 | |
2.8 | 0 | 255 | 241 | 2 | 3 | 181 | 186 | |
2.9 | 0 | 255 | 241 | 2 | 3 | 215 | 99 | |
3.0 | 0 | 255 | 241 | 2 | 3 | 249 | 250 | 1016 |
3.1 | 0 | 255 | 241 | 2 | 4 | 28 | 96 | |
3.2 | 0 | 255 | 241 | 2 | 4 | 62 | 132 | |
3.3 | 0 | 255 | 241 | 2 | 4 | 96 | 229 | |
3.4 | 0 | 255 | 241 | 2 | 4 | 130 | 70 | |
3.5 | 0 | 255 | 241 | 2 | 4 | 165 | 87 | |
3.6 | 0 | 255 | 241 | 2 | 4 | 199 | 142 | |
3.7 | 0 | 255 | 241 | 2 | 4 | 233 | 23 | |
3.8 | 0 | 255 | 241 | 2 | 5 | 11 | 64 | |
3.9 | 0 | 255 | 241 | 2 | 5 | 46 | 51 | |
4.0 | 0 | 255 | 241 | 2 | 5 | 80 | 212 | 1358 |
4.1 | 0 | 255 | 241 | 2 | 5 | 114 | 48 | |
4.2 | 0 | 255 | 241 | 2 | 5 | 148 | 87 | |
4.3 | 0 | 255 | 241 | 2 | 5 | 183 | 130 | |
4.4 | 0 | 255 | 241 | 2 | 5 | 217 | 38 | |
4.5 | 0 | 255 | 241 | 2 | 5 | 251 | 194 | |
4.6 | 0 | 255 | 241 | 2 | 6 | 29 | 136 | |
4.7 | 0 | 255 | 241 | 2 | 6 | 64 | 186 | |
4.8 | 0 | 255 | 241 | 2 | 6 | 98 | 94 | |
4.9 | 0 | 255 | 241 | 2 | 6 | 132 | 57 | |
5.0 | 0 | 255 | 241 | 2 | 6 | 166 | 221 | 1700 |
5.1 | 0 | 255 | 241 | 2 | 6 | 200 | 121 | |
5.2 | 0 | 255 | 241 | 2 | 6 | 235 | 172 | |
5.3 | 0 | 255 | 241 | 2 | 7 | 13 | 63 | |
5.4 | 0 | 255 | 241 | 2 | 7 | 47 | 219 | |
5.5 | 0 | 255 | 241 | 2 | 7 | 81 | 60 | |
5.6 | 0 | 255 | 241 | 2 | 7 | 116 | 79 | |
5.7 | 0 | 255 | 241 | 2 | 7 | 150 | 236 | |
5.8 | 0 | 255 | 241 | 2 | 7 | 184 | 117 | |
5.9 | 0 | 255 | 241 | 2 | 7 | 218 | 172 | |
6.0 | 0 | 255 | 241 | 2 | 7 | 253 | 189 | 2042 |
6.1 | 0 | 255 | 241 | 2 | 8 | 31 | 135 | |
6.2 | 0 | 255 | 241 | 2 | 8 | 65 | 230 | |
6.3 | 0 | 255 | 241 | 2 | 8 | 99 | 2 | |
6.4 | 0 | 255 | 241 | 2 | 8 | 134 | 54 | |
6.5 | 0 | 255 | 241 | 2 | 8 | 164 | 175 | |
6.6 | 0 | 255 | 241 | 2 | 8 | 202 | 118 | |
6.7 | 0 | 255 | 241 | 2 | 8 | 236 | 86 | |
6.8 | 0 | 255 | 241 | 2 | 9 | 15 | 112 | |
6.9 | 0 | 255 | 241 | 2 | 9 | 49 | 234 | |
7.0 | 0 | 255 | 241 | 2 | 9 | 83 | 51 | 2385 |
7.1 | 0 | 255 | 241 | 2 | 9 | 117 | 19 | |
7.2 | 0 | 255 | 241 | 2 | 9 | 152 | 158 | |
7.3 | 0 | 255 | 241 | 2 | 9 | 186 | 122 | |
7.4 | 0 | 255 | 241 | 2 | 9 | 220 | 103 | |
7.5 | 0 | 255 | 241 | 2 | 9 | 254 | 131 | |
7.6 | 0 | 255 | 241 | 2 | 10 | 33 | 132 | |
7.7 | 0 | 255 | 241 | 2 | 10 | 67 | 93 | |
7.8 | 0 | 255 | 241 | 2 | 10 | 101 | 125 | |
7.9 | 0 | 255 | 241 | 2 | 10 | 135 | 222 | |
8.0 | 0 | 255 | 241 | 2 | 10 | 170 | 20 | 2725 |
8.1 | 0 | 255 | 241 | 2 | 10 | 204 | 9 | |
8.2 | 0 | 255 | 241 | 2 | 10 | 238 | 237 | |
8.3 | 0 | 255 | 241 | 2 | 11 | 16 | 132 | |
8.4 | 0 | 255 | 241 | 2 | 11 | 51 | 81 | |
8.5 | 0 | 255 | 241 | 2 | 11 | 85 | 76 | |
8.6 | 0 | 255 | 241 | 2 | 11 | 119 | 168 | |
8.7 | 0 | 255 | 241 | 2 | 11 | 153 | 118 | |
8.8 | 0 | 255 | 241 | 2 | 11 | 188 | 5 | |
8.9 | 0 | 255 | 241 | 2 | 11 | 222 | 220 | |
9.0 | 0 | 255 | 241 | 2 | 12 | 0 | 105 | 3070 |
9.1 | 0 | 255 | 241 | 2 | 12 | 34 | 141 | |
9.2 | 0 | 255 | 241 | 2 | 12 | 101 | 161 | |
9.3 | 0 | 255 | 241 | 2 | 12 | 103 | 69 | |
9.4 | 0 | 255 | 241 | 2 | 12 | 137 | 139 | |
9.5 | 0 | 255 | 241 | 2 | 12 | 171 | 127 | |
9.6 | 0 | 255 | 241 | 2 | 12 | 206 | 49 | |
9.7 | 0 | 255 | 241 | 2 | 12 | 240 | 235 | |
9.8 | 0 | 255 | 241 | 2 | 13 | 18 | 188 | |
9.9 | 0 | 255 | 241 | 2 | 13 | 52 | 156 | |
10.0 | 0 | 255 | 241 | 2 | 13 | 87 | 116 | 3414 |
10.1 | 0 | 255 | 241 | 2 | 13 | 121 | 237 | |
10.2 | 0 | 255 | 241 | 2 | 13 | 155 | 78 | |
10.3 | 0 | 255 | 241 | 2 | 13 | 173 | 110 | |
10.4 | 0 | 255 | 241 | 2 | 13 | 224 | 92 | |
10.5 | 0 | 255 | 241 | 2 | 14 | 2 | 210 | |
10.6 | 0 | 255 | 241 | 2 | 14 | 36 | 242 | |
10.7 | 0 | 255 | 241 | 2 | 14 | 70 | 43 | |
10.8 | 0 | 255 | 241 | 2 | 14 | 105 | 131 | |
10.9 | 0 | 255 | 241 | 2 | 14 | 139 | 32 | |
11.0 | 0 | 255 | 241 | 2 | 14 | 173 | 0 | 3752 |
11.1 | 0 | 255 | 241 | 2 | 14 | 207 | 217 | |
11.2 | 0 | 255 | 241 | 2 | 14 | 242 | 80 | |
11.3 | 0 | 255 | 241 | 2 | 15 | 20 | 195 | |
11.4 | 0 | 255 | 241 | 2 | 15 | 54 | 39 | |
11.5 | 0 | 255 | 241 | 2 | 15 | 88 | 131 | |
11.6 | 0 | 255 | 241 | 2 | 15 | 123 | 86 | |
11.7 | 0 | 255 | 241 | 2 | 15 | 157 | 49 | |
11.8 | 0 | 255 | 241 | 2 | 15 | 191 | 213 | |
11.9 | 0 | 255 | 241 | 2 | 15 | 225 | 180 | |
12.0 | 0 | 255 | 241 | 2 | 16 | 4 | 119 | 3996 |
Edit 10/25/21: Here are the codes blinked out by the control board:
And here is a simple arduino sketch. You can probably ignore the 7-segment and rotary encoder parts, but it is successfully controlling the motor drive through a MAX485 chip on a breakout board.
/*
* Encoder on A2, A3
* Button on A1
* MAX 485: 5v to DE, D8 to DI. A and B to A and B of the treadmill controller
* TM1637 display clock on 2, data on 3
* Short out the middle "safety key" pins
* Take power from one of the treadmill controller pins (which one?)
* Treadmill header has 8 pins (left to right, latch at the bottom):
* 1 - 12v
* 2 - 12v
* 3 - A or B?
* 4 - Safety switch
* 5 - Safety switch
* 6 - A or B?
* 7 - Ground
* 8 - Ground
*/
#include <AltSoftSerial.h>
//https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
#include <SimpleTimer.h>
//https://github.com/jfturcot/SimpleTimer
#include <RotaryEncoder.h>
//http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx
//https://github.com/mathertel/RotaryEncoder
#include "OneButton.h"
//https://github.com/mathertel/OneButton
#include <TM1637Display.h>
//https://github.com/avishorp/TM1637
#include <EEPROMex.h>
//https://github.com/thijse/Arduino-EEPROMEx
const int encoderButton = A1;
const int encoderA = A2;
const int encoderB = A3;
const int dispClock = 2; //D2
const int dispData = 3; //D3
AltSoftSerial ss;
SimpleTimer timer;
OneButton encoderButt(encoderButton, true);
RotaryEncoder encoder(A2, A3);
TM1637Display display1(dispClock, dispData);
bool halt = true;
int setSpeed = 0;
int curSpeed = 0; //ramp this up until it reaches the set speed
int savedSpeed = 0;
const int speedCount = 123;
const byte b1[] = {0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16};
const byte b2[] = {0,161,195,230,8,42,76,111,145,179,213,248,26,60,94,129,163,197,231,10,44,78,112,147,181,215,249,28,62,96,130,165,199,233,11,46,80,114,148,183,217,251,29,64,98,132,166,200,235,13,47,81,116,150,184,218,253,31,65,99,134,164,202,236,15,49,83,117,152,186,220,254,33,67,101,135,170,204,238,16,51,85,119,153,188,222,0,34,101,103,137,171,206,240,18,52,87,121,155,173,224,2,36,70,105,139,173,207,242,20,54,88,123,157,191,225,4,20,54,88,123,157,191,225};
const byte b3[] = {221,16,201,186,144,116,105,188,33,197,216,18,156,188,101,79,171,182,82,43,11,210,8,154,186,99,250,96,132,229,70,87,142,23,64,51,212,48,87,130,38,194,136,186,94,57,221,121,172,63,219,60,79,236,117,172,189,135,230,2,54,175,118,86,112,234,51,19,158,122,103,131,132,93,125,222,20,9,237,132,81,76,168,118,5,220,105,141,161,69,139,127,49,235,188,156,116,237,78,110,92,210,242,43,131,32,0,217,80,195,39,131,86,49,213,180,119,195,39,131,86,49,213,180};
const int sfm[] = {0,262,314,366,418,471,523,575,628,680,732,785,837,889,941,994,1046,1098,1151,1203,1255,1308,1360,1412,1464,1517,1569,1621,1674,1726,1778,1831,1883,1935,1987,2040,2092,2144,2197,2249,2301,2355,2406,2458,2510,2563,2615,2667,2721,2772,2824,2878,2929,2981,3033,3087,3138,3190,3244,3295,3347,3401,3453,3504,3556,3610,3661,3713,3767,3818,3870,3924,3976,4027,4079,4133,4184,4236,4290,4342,4393,4447,4499,4550,4602,4656,4708,4759,4813,4865,4916,4970,5022,5074,5125,5179,5231,5282,5336,5388,5440,5493,5545,5597,5648,5702,5754,5805,5859,5911,5963,6016,6068,6120,6171,6225,6277,6329,6382,6434,6486,6539,6591,6643};
//eeprom memory locations
const int memAddress = 20;
const int memBase = 350;
void setup() {
//interrupts for encoder
PCICR |= (1 << PCIE1); // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
PCMSK1 |= (1 << PCINT10) | (1 << PCINT11); // This enables the interrupt for pin 2 and 3 of Port C.
//Serial.begin(9600);
ss.begin(9600);
timer.setInterval(100, sendSpeedPacket);
encoderButt.attachClick(startStop);
// Set Up EEPROM
EEPROM.setMemPool(memBase, EEPROMSizeNano);
//Load the stored speed value
setSpeed = EEPROM.readInt(memAddress);
display1.setBrightness(2);
updateDisplay();
}
void loop() {
// put your main code here, to run repeatedly:
timer.run();
encoderButt.tick();
readEncoder();
updateDisplay();
}
void sendSpeedPacket() {
if(halt == true) { //send the stop packet
ss.write((byte)0);
ss.write((byte)255);
ss.write((byte)241);
ss.write((byte)2);
ss.write((byte)0);
ss.write((byte)0);
ss.write((byte)221);
}
else { //send the current speed packet
ss.write((byte)0);
ss.write((byte)255);
ss.write((byte)241);
ss.write((byte)2);
ss.write((byte)b1[curSpeed]);
ss.write((byte)b2[curSpeed]);
ss.write((byte)b3[curSpeed]);
}
if (halt == true){
curSpeed = 0;
}
if (curSpeed < setSpeed && halt == false) { //this ramps the sent speed up until it hits the set speed
curSpeed++;
}
if (curSpeed > setSpeed && halt == false) { //this immediately reduces the sent speed to the set speed
curSpeed = setSpeed;
}
//Serial.print(halt);
//Serial.print(" ");
//Serial.println(curSpeed);
}
void startStop () {
halt = !halt;
EEPROM.writeInt(memAddress, setSpeed); //save the speed every time the button is pushed... so that on next power up it is not zero
}
void readEncoder() {
static int pos = 0;
int newPos = encoder.getPosition();
if (pos > newPos) {
setSpeed = setSpeed - 1;
}
else if (pos < newPos) {
setSpeed = setSpeed + 1;
}
pos = newPos; //--keep
setSpeed = constrain(setSpeed, 0, speedCount);
}
ISR(PCINT1_vect) {
encoder.tick(); // just call tick() to check the state.
}
void updateDisplay() {
display1.showNumberDecEx(sfm[setSpeed], 0, true); //Set time should always show on display 1
// Serial.print("Minutes: ");
// Serial.print(seconds / 60);
// Serial.print("Seconds: ");
// Serial.println(seconds & 60);
}
Update 11/2022 – here’s a list of some of the unique commands I saw. I didn’t test or try to capture the incline setting, but if we’re lucky maybe it’s in here:
These are the unique commands and responses I recorded just after powering up:
Source Unique Message Count Comment
Panel 0 255 14 0 14 1
Motor 128 0 14 0 5 1
Panel 0 255 9 64 0 95 241 2
Motor 128 0 9 0 107 2
Panel 0 255 159 0 9 30 Common while idle and running
Motor 128 0 159 64 0 0 160 30 Probably reply to 0 255 159 09
Panel 0 255 143 64 0 0 187 81 Speed command? 143
Motor 128 0 143 0 238 81
And here are the unique commands while it was running at a steady .5 mph:
Source Message Count Comment
Panel 0 255 143 64 0 133 8 77 Speed command? 143
Motor 128 48 143 0 48 77 Same acknowledgement to speed command as 1 mph
Panel 0 255 111 64 0 20 14 76 Same while running at 1 mph
Motor 128 48 111 0 69 76 same response as running at 1 mph
Panel 0 255 159 0 9 23 Common while idle or running
Motor 128 48 159 64 0 37 56 11
Motor 128 48 159 64 0 165 180 10
Will your end outcome be a multi speed belt sander or a single speed? I like the idea of multi speeds for sanding different materials.
Bill
It’ll be variable speed, perhaps with some presets, also maybe with a readout that gives surface feet/min just for fun!
Outstanding!
I just scrapped a Horizon T203 with this very board. My hope was to reuse the control board just to keep it simple, you have enabled that.
Thanks again! This is great work and thanks so much for publishing it!
Just finished setting everything up… works like a champ. I’m looking to add the display in a couple of days…. Thanks for some great work!… Dave / Rapid City, SD
Fantastic! What are you using the motor for?
I have replace the A/C motor in a shopsmith 5 in 1 power tool… Table saw, drill press, lathe, bandsaw, etc. I just packaged everything back in the headstock and it runs much more smoothly than the old v-belt system for variable speed it had. The only software change I made was to put a pause in betwee display updates to eliminate some flicker I had.
Here’s a link to the Shopsmith forum where people are playing with lots of different treadmills. I haven’t seen anyone else using this one…. the motor seems quite beefy though.
https://www.facebook.com/groups/464223824041406/?hoisted_section_header_type=recently_seen&multi_permalinks=1632642590532851
Just curious… do you know why the upper RPM tops out?…
I was just wondering if I taped every other tooth on the RPM sensor… Would that possibly trick the processor into going twice as fast?
I’m sure the limit is there so you don’t fly off the treadmill… I think that’s worth a try, my intuition says that should work but I don’t know if it has any other limiting built in – like a current limit – I think it would be trying to accelerate twice as hard as usual, right?
Thanks for the post this is what I was looking for to salvage some treadmills and make something else.
Thank you so much for posting this. Just picked up a treadmill for free and this is exactly the shortcut I needed. Thank you for posting!
Thank you very much for posting all this very useful data, I have used a LCD keypad shield over an Arduino UNO and an external MAX485 module to control my treadmill motor and it work perfectly.
But I noticed that my “speedCount” is limited to 115 and not 123. If I go to 116 the speed of the motor would be decreasing…
By any chance, did you get the incline motor codes for controlling it. My guess would be that the third byte would be a “1” instead of “2” and with 2 more bytes for the elevation…
Any help here would be greatly appreciated.
Thank you
I’m so happy to hear that this was helpful. Unfortunately I didn’t bother with the incline at all – I had taken it all apart before I did this and didn’t have the incline motor hooked up to test with. I actually think I tossed the control panel as well… but if you’re willing to experiment I think you could repeat what I did and capture more of the packets. The logic analyzer was very cheap, or you can probably accomplish the same thing with the MAX485 and a different arduino sketch to listen in if you still have the original control.
Unfortunately someone just gave me the 2.5HP motor, the incline motor part and the supply/control board cut from the rest that was disposed in the garbage… 🙁
So there is no way for me to test the communication with the control panel.
Then I can only guess the command for the incline motor…
I will try to send from my Arduino board :
0 255 241 0 0 0 0
0 255 241 0 0 1 1
0 255 241 0 1 0 0
0 255 241 0 1 1 1
0 255 241 1 0 0 0
0 255 241 1 0 1 1
0 255 241 1 1 0 0
0 255 241 1 1 1 1
0 255 241 3 0 0 0
0 255 241 3 0 1 1
0 255 241 3 1 0 0
0 255 241 3 1 1 1
And see if there is any movement from the incline motor!
I will keep you posted if I get any positive result.
I found my original logs. I didn’t push the incline buttons, so I can’t promise the incline commands are in there, but maybe one of these will work. I don’t know if the panel would command any incline angle when it powers up, or perhaps it just requests the angle and displays it?
Hopefully the formatting on this works out.It did not, instead check the bottom of this post I added them there.
The first two bites are always the same from the panel, so those should be 0 and 255. My guess is the third byte determines what the command is, then the other bytes are parameters for it. In the speed example it was 3 bytes, but I noticed some of these were shorter. Apparently I left myself a note on one of them that I thought might be incline: 0 255 111 64 0 20 14.
Good luck, and if you figure it out let me know, I’ll update the post!
I have tried : 0 255 111 64 0 20 14
If sent repeatedly at 100ms interval, the control board show the “Communication Abnormal” LED sequence.
There is no issue with any other speed sequence bytes.
Same issue with : 0 255 143 64 0 133 8
So good news – I found a box with the control panel board and all the buttons in it, I guess I didn’t throw it out. Do you want me to send it to you?
Unfortunately, I was unable to find the commands for the incline motor.
If someone can find this information, it would be greatly appreciated!
Here is what I found on my treadmill. Most of the speed commands listed in the article matched up with my Livestrong LS8.0T, so it’s probably pretty close for other Johnson treadmills. The last column is the incline percent.
0, 255, 246, 2, 0, 40, 112, 0
0, 255, 246, 2, 0, 80, 49, 0.5
0, 255, 246, 2, 0, 120, 14, 1
0, 255, 246, 2, 0, 160, 179, 1.5
0, 255, 246, 2, 0, 200, 117, 2
0, 255, 246, 2, 0, 240, 205, 2.5
0, 255, 246, 2, 1, 24, 65, 3
0, 255, 246, 2, 1, 64, 134, 3.5
0, 255, 246, 2, 1, 104, 185, 4
0, 255, 246, 2, 1, 144, 130, 4.5
0, 255, 246, 2, 1, 184, 189, 5
0, 255, 246, 2, 1, 224, 122, 5.5
0, 255, 246, 2, 2, 8, 47, 6
0, 255, 246, 2, 2, 48, 83, 6.5
0, 255, 246, 2, 2, 88, 81, 7
0, 255, 246, 2, 2, 128, 236, 7.5
0, 255, 246, 2, 2, 168, 211, 8
0, 255, 246, 2, 2, 208, 146, 8.5
0, 255, 246, 2, 2, 248, 173, 9
0, 255, 246, 2, 3, 32, 228, 9.5
0, 255, 246, 2, 3, 72, 230, 10
0, 255, 246, 2, 3, 112, 154, 10.5
0, 255, 246, 2, 3, 152, 226, 11
0, 255, 246, 2, 3, 192, 37, 11.5
0, 255, 246, 2, 3, 232, 26, 12
Please find my speed and incline tables in my github: https://github.com/eborchardt/treadmillData.
Hope it helps!
Sorry, I’m not sure why it showed up like that. I’ll try again.
0, 255, 246, 2, 0, 40, 112, 0
0, 255, 246, 2, 0, 80, 49, 0.5
0, 255, 246, 2, 0, 120, 14, 1
0, 255, 246, 2, 0, 160, 179, 1.5
0, 255, 246, 2, 0, 200, 117, 2
0, 255, 246, 2, 0, 240, 205, 2.5
0, 255, 246, 2, 1, 24, 65, 3
0, 255, 246, 2, 1, 64, 134, 3.5
0, 255, 246, 2, 1, 104, 185, 4
0, 255, 246, 2, 1, 144, 130, 4.5
0, 255, 246, 2, 1, 184, 189, 5
0, 255, 246, 2, 1, 224, 122, 5.5
0, 255, 246, 2, 2, 8, 47, 6
0, 255, 246, 2, 2, 48, 83, 6.5
0, 255, 246, 2, 2, 88, 81, 7
0, 255, 246, 2, 2, 128, 236, 7.5
0, 255, 246, 2, 2, 168, 211, 8
0, 255, 246, 2, 2, 208, 146, 8.5
0, 255, 246, 2, 2, 248, 173, 9
0, 255, 246, 2, 3, 32, 228, 9.5
0, 255, 246, 2, 3, 72, 230, 10
0, 255, 246, 2, 3, 112, 154, 10.5
0, 255, 246, 2, 3, 152, 226, 11
0, 255, 246, 2, 3, 192, 37, 11.5
0, 255, 246, 2, 3, 232, 26, 12
Sorry about the format. I’ll upload it to my github later today, but here’s another try:
{0,255,246,2,0,40,112,0},
{0,255,246,2,0,80,49,0.5},
{0,255,246,2,0,120,14,1},
{0,255,246,2,0,160,179,1.5},
{0,255,246,2,0,200,177,2},
{0,255,246,2,0,240,205,2.5},
{0,255,246,2,1,24,65,3},
{0,255,246,2,1,64,134,3.5},
{0,255,246,2,1,104,185,4},
{0,255,246,2,1,144,130,4.5},
{0,255,246,2,1,184,189,5},
{0,255,246,2,1,224,122,5.5},
{0,255,246,2,2,8,47,6},
{0,255,246,2,2,48,83,6.5},
{0,255,246,2,2,88,81,7},
{0,255,246,2,2,128,236,7.5},
{0,255,246,2,2,168,211,8},
{0,255,246,2,2,208,146,8.5},
{0,255,246,2,2,248,173,9},
{0,255,246,2,3,32,228,9.5},
{0,255,246,2,3,72,230,10},
{0,255,246,2,3,112,154,10.5},
{0,255,246,2,3,152,226,11},
{0,255,246,2,3,192,37,11.5},
{0,255,246,2,3,232,26,12}
So I had mentioned in an earlier post if it was possible to increase the speed. It seemed to top out a little below a readout of 3K… My thought was to reduce the number of teeth in the speed sensor wheel. I covered every other gap with silver reflecting tape. (Aluminum duct tape that’s very lightweight). I’m now able to get just over 5K and I can bring the speed up much more quickly than before and it doesn’t keep shutting down… I’m assuming their firmware just thinks it’s turning half as fast?… anyway definitely suits my purpose…. thanks again… Dave L.
I think you’re absolutely right, that’s great and thanks for updating here!
I am completely ignorant of electronics. I have the same motor but no electronics and I wanted to use it as the motor for a woodworking machine. Would you be willing to build one of these controllers with a simple speed dial and tachometer? And if so how much would you chard for it?
Hey Derrick – if you don’t have the control from the treadmill then this project won’t be of any use. However there are some other options to run a DC motor like this, maybe start here and see what you can come up with: https://dazecars.com/dazed/variable.html
I have picked up a treadmill for free and to my pleasant surprise it had a 750W (probably exaggerated) brushless motor in it. I was however confronted with the same sort of problem you had regarding a way to control it. I have worked out the communication in a similar way you have, it just uses a Uart tx rx. The messages are different format though. They have an identifier, message length , function, variable and a checksum. Thusfar I have been able to run it from an arduino but not consistently to a point I was giving up hope to be able to use it. But you gave me some inspiration to give it another shot. It’s a brushless motor after all. Controller is a Chinese MKS 18.0096 BLDC-KS-P. I will try again and post my findings here.
After retrying to setup the controller and motor, I unfortunately found out that it had develop a problem. Maybe some swarf or something fell into the circuit while it was laying around. even though I did blew off the dust, I could not make it run again. I found out that the micro was shorting to ground. Maybe i will try to replace that at some point with something compatible. Nevertheless, I still had some of the captures so I will post the commands I have figured out. First command is an init it appears. maybe assinging the address or something {254;247}, next there are 2 times another init {247; 248;4;1;1;1;7;253} . All command starts with 247;248 and then a message length, in this case 4 bytes 1;1;1;7. The first two bytes are always 1. The latter byte is a checksum 256 modulo: 4+1+1+1=7. To terminate, the message always ends with 253. Then the next message is another init. {247;248;18;1;1;2;0;0;0;0;13;150;0;0;12;183;53;19; 36;130;106;253} . I don’t understand what happens here, maybe this is to set max accelaration, deceleration, but is needed to make it work.
And now the speed commands begin.
247;248;8;1;1;3;0;1;152;0;166;253
247;248;8;1;1;3;1;1;152;0;166;253
247;248;8;1;1;3;0;1;152;0;167;253
247;248;8;1;1;3;0;1;152;0;166;253
247;248;8;1;1;3;2;1;16;0;30;253
247;248;8;1;1;3;0;1;16;0;32;253
247;248;8;1;1;3;0;1;16;0;30;253
So, again, these start with 247;248; 8 byte message; 1;1 and function 3, then a 0 or 1 or 2 (don’t understand) and then 2 speed bytes in MSB,LSB order, a 0 and the checksum and 253. If I recall correctly I was able to go up to 5000 or something. I did however see on various occasions, it would spin out of control and then error and stop. So I hope if someone who comes accross these walking pads, can use the information. I’ll be looking out for more free threadmills in my area.
Hey Elliotmade, thanks for the sharing, I found this very helpful. I acquired a Matrix 7xi treadmill (also made by Johnson Fitness) but all the upper control panel electronics are dead. I was able to use an Arduino Uno and your code to control the motor and speed! There is no way I could’ve figured out the correct packets as sequence without your data! I have discovered that bytes 5, 6, and 7 must match the values in the table exactly, or they are ignored. I am still trying to get incline working correctly, though, even with Eric’s table I can only get the incline motor to move down, nothing else. Anyways, thanks again for sharing!