Reading/interpreting tach signals from treadmill (speed/incline)

I'm not sure where to begin here. I can control the speed and incline but have no idea how to read/interpret the feedback. A nudge in the right direction is all I ask. Meanwhile, I'll keep searching the forum. All the info on the signals I have (full controller info attached):

Speed:
This connection is for the reed switch wire. This allows the tach signal to be received by the controller and passed to the console wire harness, where it is sent up the GREEN wire. Voltage is a pulsing 5 VDC when the treadmill is running. When the treadmill is at rest, this voltage may be 0 VDC or 5 VDC, depending on the whether the magnet is closing the reed switch or not.

Incline:
This connection is for the incline sensor. While it will usually be a reed switch, an optic switch can also be connected. 5 VDC will be present across the open switch. Whenever the switch closes, 0 VDC will be measured. As the incline motor turns, this voltage will pulse on and off. The number of pulses is used by the console to determine how far it has changed the incline. When the incline is at rest, this voltage may measure 0 VDC or 5 VDC, depending on the position of the switch in relation to the magnet (or optic disk).

Thanks, any help is appreciated!

[modified: removed images in lieu of PDFs]

Mc-2100 Manual Control.pdf (1.17 MB)

MC2100_rev_engrd.pdf (248 KB)

This connection is for the reed switch wire. This allows the tach signal to be received by the controller and passed to the console wire harness, where it is sent up the GREEN wire. Voltage is a pulsing 5 VDC when the treadmill is running. When the treadmill is at rest, this voltage may be 0 VDC or 5 VDC, depending on the whether the magnet is closing the reed switch or not.

So, have you connected this wire to the Arduino? Are you seeing the pin go HIGH and LOW? Does the state switch often enough that polling is not sufficient? Have you tried using an interrupt?

The first part of the problem is connecting the sensor to the Arduino correctly. The second part is reading the sensor, at least some times. The third part is reading it every time. The fourth part is making sense of/using the data that you get. We have no clue where you are in this progression.

Totally understood. Sorry for not being more specific... I'm using an Arduino UNO Rev. 3 I've attached all the info I have on the mc-2100.
Below are two sketches.

Both I get power/ground from the mc2100:

  • power from the mc2100 (say it should be 9VDC but more like 12VDC) by connecting RED to Vin
  • grounding using the mc2100 ground by connecting BLACK to GND (just above Vin)

For incline I'm using the following setup:

  • 22K resistor between VIOLET (incline sensor signal from mc2100) and pin 2
  • sending 5VDC to the mc2100 to trigger incline/decline through pins 10/11 down wires ORANGE/YELLOW

I can count the pulses and seem to be getting a fairly accurate reading where 5 pulses = 1 deg of incline. The only issue I have is figuring out the current incline as the sense signal only seems to cover define the current distance being moved. As of now, I think I just need to store the position or always zero out the incline on shutdown.

The second, reading the speed, is where I have some problems.

  • The GREEN speed sense from the controller connected to pin 3.
  • The BLUE wire connected to pin 9 and I'm using Timer 1 to set the PWM.

Here I can control the power but have no clue how to read the current speed.

Treadmill incline sketch

// treadmill incline

const int sensorPin = 2;
const int sensorInterruptPin = 0;

const int elevatUp = 10;
const int elevatDn = 11;

const int timeoutValue = 5;

volatile unsigned long lastPulseTime;
volatile unsigned long interval = 0;
volatile int timeoutCounter;
volatile int pulseCount;

volatile int currentPosition = 7;
const int maxPosition = 50;
int inclineCommand = 0;
boolean newCommand = false;

void setup(){
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH); 
  pinMode(elevatUp, OUTPUT);
  pinMode(elevatDn, OUTPUT);
  digitalWrite(elevatUp, LOW);
  digitalWrite(elevatDn, LOW);
  
  attachInterrupt(sensorInterruptPin, sensorInterrupt, RISING);
  
  Serial.begin(9600);
  
  lastPulseTime = micros();
  timeoutCounter = 0;
  pulseCount = 0;
}

void sensorInterrupt(){
  unsigned long now = micros();
  interval = now - lastPulseTime;
  lastPulseTime = now;
  timeoutCounter = timeoutValue;
  pulseCount = pulseCount + 1;
  currentPosition = currentPosition + inclineCommand;
  reachedFinish();
}

boolean reachedFinish(){
  if(currentPosition == 0 || currentPosition == maxPosition){
    inclineCommand = 0;
    inclineStop();
    if(currentPosition == maxPosition){      
      Serial.println(" -- Now in Max position"); 
    } else { 
      Serial.println(" -- Now in Min position"); 
    }      
    return true;
  } else {
    return false; 
  }
}

void inclineStop(){
  digitalWrite(elevatUp, LOW);
  digitalWrite(elevatDn, LOW);
  Serial.println("Stop");
  pulseCount = 0; 
}

void inclineDecrease(){
  if(currentPosition - 1 > 0){
    inclineStop();
    digitalWrite(elevatDn, HIGH);
    Serial.println("Decrease");
  } else {
    reachedFinish();
  }
}

void inclineIncrease(){
  if(currentPosition + 1 < maxPosition){
    inclineStop();
    digitalWrite(elevatUp, HIGH);
    Serial.println("Increase");
  } else {
    reachedFinish();
  }
}

void loop(){ 
   if(newCommand){
     newCommand = false;
     
     switch (inclineCommand) {      
      case 1:
        inclineIncrease();
        break;
      case -1: 
        inclineDecrease();
        break;
      default:
        inclineStop();
        break;        
     }
  }
  
  if (timeoutCounter != 0){
    --timeoutCounter;
    Serial.println("Interval: "+(String)interval);
    Serial.println("Pulses: "+(String)pulseCount);
    Serial.println("Current position: "+(String)currentPosition);
    Serial.println("Incline direction: "+(String)inclineCommand);
  } 
  delay(500);
}

void serialEvent() {
  String content = "";
  char character;

  while(Serial.available()) {
      character = Serial.read();
      content.concat(character);
  }

  if (content != "") {
      inclineCommand = content.toInt();
      newCommand = true;
  }
}

Treadmill speed sketch

#include <TimerOne.h>

const int speedPin = 9;
const int maxDuty = 870;
const int minDuty = 164;
const int stpDuty = 0;
const int mphIntervals = 71; // guessing here 
const int sensorPin = 3;
const int sensorInterruptNum = 1;
const int timeoutValue = 5;

/* variable delcorations and defaults - start */
int currentSpeed = 0;
float speedSetTo = 0;

volatile unsigned long lastPulseTime;
volatile unsigned long interval = 0;
volatile int timeoutCounter;
volatile int pulseCount;

int currentCommand = 0;
int inputCommand = 0;
boolean newCommand = false;

void setup(){
  Timer1.initialize(50000); // initialize timer1, and set a 1/2 second period
  Timer1.pwm(speedPin, 0); // max 870 (85%)
  Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt 
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH); 
  attachInterrupt(sensorInterruptNum, sensorInterrupt, RISING);  
  Serial.begin(9600);
  lastPulseTime = micros();
  timeoutCounter = 0;
}

void callback(){
  
   if(newCommand){
     currentCommand = inputCommand;
     newCommand = false;
   
     if(currentCommand <= maxDuty){       
       
       switch(currentCommand){
         case maxDuty:
           speedSetTo = maxDuty;
           break;
         case 0: 
           speedSetTo = stpDuty;
           break;
         default:
           // float newSpeed = currentCommand;
           speedSetTo = currentCommand;
           break;
       }       
       Timer1.setPwmDuty(speedPin, speedSetTo);       
     }       
     Serial.print("Change speed to: ");
     Serial.println(speedSetTo);
     Serial.println((float)interval);
   }
}

void sensorInterrupt(){
  unsigned long now = micros();
  interval = now - lastPulseTime;
  lastPulseTime = now;
  timeoutCounter = timeoutValue;
  pulseCount = pulseCount + 1;
}

void loop(){
  if (timeoutCounter != 0){
    --timeoutCounter;
    Serial.println((float)interval);
  }
  delay(500);
}


void serialEvent() {
  String content = "";
  char character;

  while(Serial.available()) {
      character = Serial.read();
      content.concat(character);
  }

  if (content != "") {
      inputCommand = content.toInt();
      newCommand = true;
  }
}

[modified: attached PDFs to original post: Controller Manual and possible Reverse Engineered schematic of mc-2100]

I had the same problem.

I found out by reading the mc2100 datasheet, that the controller uses the green wire to send small digital signals to the control panel as well as the tach pulse that is sent up the green wire.

You can verify that by reading the signal by manually rotating the wheel past the sensor and reading its pulse. If you start the motor running, you can completely remove the sensor and you get your crazy (additional digital data signals) readings.

I first made a low pass filter to remove those additional signals, but they seemed to be around the same frequency as ~5mph sensor readings.

In the end, I added a hall effect switch that uses the same magnet the tach sensor (reed switch) does. The output from the hall effect sensor runs to an optocoupler on my circuit, then to my board.

Dave

drobe011:
If you start the motor running, you can completely remove the sensor and you get your crazy (additional digital data signals) readings.

It strikes me as pretty unlikely that the manufacturers would lie about the way the reed switch works, and also pretty unlikely that they would go to all the bother of trying to piggy-back a digital signal on a circuit that was being mechanically shorted by a reed switch at uncontrolled intervals. It's just not a sensible thing to do, and everything else that has been described seems perfectly sensible and straightforward. More likely IMO, what you were picking up is some combination of electrical noise picked up by the wiring loop and/or a floating input.

The MC-2100 has its own processor and software, which allows it to communicate with the console. This is done by a small digital signal carried by the GREEN tach wire. By entering calibration mode on the console, two alternate screens can be accessed which give information on the controller, including the status of the troubleshooting LED, motor voltage, and motor amperage. This greatly increases the amount of troubleshooting that can be done without removing the treadmill’s motor hood.
Some MC-2100 controllers will have the transformer mounted on the circuit board as shown above. Other versions..........

The above is an excerpt from the 'datasheet' on page 1. I found it crazy as well, and initially just thought it was motor noise.

Dave

I've been working with a bag of old components and jumpers. I've got some basics on order now. Once I they are delivered I'll start trying to tap the signals sent to and from the console.

I'll keep you posted.

Attached is a pic of the mc2100.

I keep trying but I think I'm going down the wrong path.

New Speed Sketch [modified: corrected var types]

#include <TimerOne.h>

const int speedPin = 9;
const int maxDuty = 870;
const int minDuty = 164;
const int stpDuty = 0;
const int mphIntervals = 71; // guessing here 
const int sensorPin = 3;
const int sensorInterruptNum = 1;
const int timeoutValue = 20;
/* variable delcorations and defaults - start */
int currentSpeed = 0;
int speedSetTo = 0;

volatile int time = 0;
volatile int time_last = 0;
volatile int timeoutCounter = 0;
volatile unsigned long rpm = 0;
volatile int pulseCount;

int currentCommand = 0;
int inputCommand = 0;
boolean newCommand = false;

void setup(){
  Timer1.initialize(50000); // initialize timer1, and set a 1/2 second period
  Timer1.pwm(speedPin, 0); // max 870 (85%)
  Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt
  
  pinMode(sensorPin, INPUT);  
  attachInterrupt(sensorInterruptNum, sensorInterrupt, RISING); 
  
  Serial.begin(9600);
  pulseCount = 0;
  rpm = 0;
}

void callback(){
  
   if(newCommand){
     currentCommand = inputCommand;
     Serial.println(inputCommand);
     newCommand = false;
   
     if(currentCommand <= maxDuty){       
       
       switch(currentCommand){
         case maxDuty:
           speedSetTo = maxDuty;
           break;
         case 0: 
           speedSetTo = stpDuty;
           break;
         default:
           // float newSpeed = currentCommand;
           speedSetTo = currentCommand;
           break;
       }       
       Timer1.setPwmDuty(speedPin, speedSetTo);
       Serial.print("Change speed to: ");
       Serial.println(speedSetTo);
     }       
   }
}

void sensorInterrupt(){
  time = (micros() - time_last);
  time_last = micros();  
  timeoutCounter = timeoutValue;
  rpm = 60*(1000000/time);
}

void loop(){
  if (timeoutCounter != 0){
    --timeoutCounter;
    Serial.print("RPM: ");
    Serial.println(rpm);
  }
  delay(100);
}


void serialEvent() {
  String content = "";
  char character;

  while(Serial.available()) {
      character = Serial.read();
      content.concat(character);
  }

  if (content != "") {
      inputCommand = content.toInt();
      newCommand = true;
  }
  
}

Serial output:

376
Change speed to: 376
RPM: -18218
RPM: -1803
RPM: -1803
RPM: 254
RPM: 254
RPM: 516
RPM: 516
RPM: 516
RPM: 516
RPM: 516
RPM: 516
RPM: 516
RPM: 516
RPM: 66
RPM: 66
RPM: 66
RPM: 66
RPM: 66
RPM: 66
RPM: 66
RPM: 5653
RPM: 604
RPM: 604
RPM: 604
RPM: 604
RPM: 604
RPM: 604
RPM: 604
RPM: 604
RPM: 604
RPM: 604
RPM: 60
RPM: 60
RPM: 60
RPM: 60
RPM: 60
RPM: 60
RPM: 60
RPM: 60
RPM: 60
RPM: 60
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 57
RPM: 41
RPM: 41
RPM: 41
RPM: 41
RPM: 41
RPM: 41
RPM: 41
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 80
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 25
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
RPM: 18
445
Change speed to: 445
RPM: 2
RPM: 2
RPM: 22321
RPM: -12905
RPM: -7907
RPM: -1977
RPM: 23749
RPM: 7816
RPM: 13829
RPM: 4188
RPM: -21026
RPM: -9121
RPM: 21157
RPM: 31267
RPM: 4886
RPM: -32388
RPM: 7993
RPM: 4789
RPM: 7816
RPM: -23930
RPM: 3765
RPM: 2028
RPM: 4082
RPM: -23930
RPM: 22852
RPM: 2645
RPM: 30617
RPM: 1840
RPM: 13229
RPM: -9121
RPM: 30364
RPM: -32054
RPM: -31904
RPM: 1699
RPM: -13885
RPM: 3358
RPM: 1213
RPM: -26269
RPM: -20778
RPM: 6542
RPM: 2645
RPM: 3859
RPM: 741
RPM: 1741
RPM: 1728
RPM: 1874
RPM: 13979
RPM: 1245
RPM: 5131
RPM: 1271
RPM: 716
RPM: 936
RPM: 14251
RPM: 1681
RPM: 3604
RPM: 3826
RPM: 22852
RPM: 4096
RPM: 4865
RPM: 8871
RPM: 30005
RPM: -12145
RPM: -24553
RPM: 2957
RPM: 513
RPM: -24886
RPM: -24886
RPM: 302
RPM: 29411
RPM: 410
RPM: 15608
RPM: 3357
RPM: -31904
RPM: -7907
RPM: 23474
RPM: 12647
RPM: 4043
RPM: 7816
RPM: 9114
RPM: 1780
RPM: 604
RPM: 20862
RPM: 603
RPM: 611
RPM: 1202
RPM: 1202
RPM: 401
RPM: 405
RPM: 3795
RPM: 1642
RPM: 1642
RPM: 1224
RPM: 1224
RPM: 1844
RPM: 1163
RPM: 4534
RPM: -881
RPM: 7816
RPM: 31380
RPM: 935
RPM: 935
RPM: 323
RPM: 323
RPM: 1202
RPM: 1186
RPM: 1186
RPM: 1234
RPM: 1159
RPM: 605
RPM: 1173
RPM: 1173
RPM: 1130
RPM: 1130
RPM: 1753
RPM: 522
RPM: 1731
RPM: 397
RPM: 397
RPM: 397
RPM: 397
RPM: -1977
RPM: 1955
RPM: 530
RPM: 500
RPM: 1804
RPM: 362
RPM: 362
RPM: 362
RPM: 362
RPM: 362
RPM: 362
RPM: 103
RPM: 103
RPM: 103
RPM: 199
RPM: 199
RPM: 355
RPM: 13158
RPM: 3309
RPM: 1829
RPM: 1829
RPM: 300
RPM: 607
RPM: 3661
RPM: 4059
RPM: 4059
RPM: 276
RPM: 605
RPM: 1134
RPM: 1134
RPM: 1134
RPM: 1210
RPM: 1210
RPM: -29305
RPM: -11072
RPM: 713
RPM: 902
RPM: 403
RPM: 1197
RPM: 1197
RPM: 1197
RPM: 1197
RPM: 1197
RPM: 1197
RPM: 1197
RPM: 1197
RPM: 1197
RPM: 66
RPM: 1205
RPM: 4342
RPM: 697
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 366
RPM: 42
RPM: 42
RPM: 258
RPM: 894
RPM: 894
RPM: 1205
RPM: 720
RPM: 514
RPM: 604
RPM: 604
RPM: 297
RPM: 297
RPM: 403
RPM: 403
RPM: 403
RPM: 403
RPM: 403
RPM: 403
RPM: 403
RPM: 403
RPM: 403
RPM: 403
RPM: 57
RPM: 1290
RPM: 1290
RPM: 1290
RPM: 1290
RPM: 1290
RPM: 1290
RPM: 1290
RPM: 1253
RPM: 1253
RPM: 1253
RPM: 1253
RPM: 149
RPM: 149
RPM: 149
RPM: 149
RPM: 149
RPM: 120
RPM: 120
RPM: 120
RPM: 120
RPM: 120
RPM: 120
RPM: 92
RPM: 92
RPM: 92
RPM: 200
RPM: 1129
RPM: 1129
RPM: 1129
RPM: 172
RPM: 172
RPM: 172
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 199
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 20
RPM: 51
RPM: 51
RPM: 51
RPM: 51
RPM: 51
RPM: 51
RPM: 51
RPM: 79
RPM: 79
RPM: 79
RPM: 79
RPM: 79
RPM: 79
RPM: 79
RPM: 79
RPM: 79
RPM: 79
RPM: 59
RPM: 59
RPM: 59
RPM: 59
RPM: 59
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 26132
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
RPM: 30
0
Change speed to: 0
  rpm = 60*(1000000/time);

Constants are treated as ints, unless you specify otherwise. 1000000 doesn't look like it will fit in an int, does it?

1000000UL, on the other hand, will easily fit in an unsigned long.

  time = (micros() - time_last);
  time_last = micros();

Why the hell are thse floats? Time is integral.

  rpm = 60*(1000000/time);

Had rpm set as float in one of my last test when converting. Decided print the full value but forgot to change the type.

  time = (micros() - time_last);
  time_last = micros();

Bad copy/paste job from: Arduino Tachometer - Software | PyroElectro - News, Projects & Tutorials

Good catches both but even with those changes I seem to get some crazy readings. For instance, sensorInterrupt isn't even called until I push to what I'm assuming is close to 4 mph.

  • thanks

Mod80:
even with those changes ...

... which we can't see ...

Just updated the sketch. I'll update the serial output in the morning.

Among the products listed on the site, which one is the best? https://rainydayfitness.com/best-treadmill/

kevinspear:
Among the products listed on the site, which one is the best? https://rainydayfitness.com/best-treadmill/

For what?

And what does your question have to do with the Arduino? This isn't the Consumer Reports web site.