Treadmill control - almost works!

Hello!

I’ve been playing with a Duemilanove with LCD + 5 button keypad shield for a couple weeks now and wanted to try interfacing it with a treadmill who’s control panel quit. The code I’ve written (actually swiped and modified) pretty much works with one exception.

The buttons are used like this:

Select button - starts and/or stops the treadmill (just toggles back and forth)
UP button - incline up signal
DOWN button - incline down signal
RIGHT button - increase speed
LEFT button - decrease speed

All the buttons work as expected except the down button and it’s repeatable. After I’ve started the program and increased the incline and speed a couple notches - if I press the incline down button, nothing happens until I press another button and it seems to work like this:

DOWN then RIGHT - incline value sets to zero (regardless of previous value) and speed value increments
DOWN then LEFT - incline value sets to zero (regardless of previous value)and speed value decrements
DOWN then UP - incline value sets to one (regardless of previous value)
DOWN then DOWN - nothing happens

The other symptom is the pin for the incline down signal never goes high, all other pins operate as expected.

My code: (any input is appreciated)

/// ===================================================================
// Treadmill control
// Code kernel shamelessly swiped from code written by kiwix on arduino
// forum:  www.arduino.cc
// -------------------------------------------------------------------
// * Modified to use a patched LiquidCrystal library
// * Alternative routine to detect keys
// ===================================================================
#include <stdio.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// ===================================================================
// hardware settings:

#define rightKey  0		
#define upKey     1
#define downKey   2
#define leftKey   3
#define selectKey 4
#define NUM_KEYS  5

// ===================================================================
// I/O Settings:

#define speedPin 3			// Speed is PWM output on pin 3
#define inclineUpPin 10			// Incline up is pin 10 set high
#define inclineDownPin 12		// Incline down is pin 12 set high

// ===================================================================
// Program parameters

#define minSpd   0		// minimum speed
#define maxSpd 10		// max speed
int speedValue = 0;		// set initial speed value to 1

#define minIncl   0		// minimum incline
#define maxIncl  10		// max incline
int inclineValue = 0;		// set initial incline to 1 (flat)

#define inclineOutTime 2000     // set incline motor on-time to 2 seconds per button press

boolean inMainProgram  =  true; // test for main program running

void setup()
{
  lcd.begin(16, 2);
  pinMode(inclineUpPin, OUTPUT);		// Set Incline up pin as output
  pinMode(inclineDownPin, OUTPUT);		// Set incline down pin as output

void loop()
{
  int keyEvent = detectKey();	// set keyEvent to value returned from detectKey(0 to 4)
  if (keyEvent >= 0)		// if the value of keyEvent is >= 0 then decide what to do:
  {
    switch (keyEvent)          // use value of keyEvent
    {

    case upKey:	                  // if keyEvent = upKey
      if (!inMainProgram)         // if inMainProgram is set false
      {
        if (inclineValue >= maxIncl)  // check we haven't exceeded max incline
        {
          (inclineValue = maxIncl);   // if we have then we can't go further
          break;
        }                              // otherwise:
        inclineValue++;               // increment the value
        updateScreen();               // update the screen with the new value
        inclineUp();                  // and move the treadmill up
      }
      break;                         

    case downKey:                          // if keyEvent = downKey
      if (!inMainProgram)                  // if inMainProgram is still false:
      {
        if (inclineValue <= minIncl);      // check to see we're not lower than the minimum
        {
          (inclineValue = minIncl);        // if so then go no further
          break;
        }                                  // otherwise:
        inclineValue--;                    // decrement the value
        updateScreen();                    // update the screen with the new value
        inclineDown();                     // move the treadmill down
      }
      break;

    case rightKey:                         // if keyEvent = rightKey
      if (!inMainProgram)
      {
        if (speedValue >= maxSpd)          // check to see if we're past max
        {
          (speedValue = maxSpd);           // if so, go no faster
          break;
        }                                  // otherwise:
        speedValue++;                      // we wanna go faster, increment the value
        updateScreen();                    // update the screen
        speedSet();                        // speed er up
      }
      break;

    case leftKey:                          // if keyEvent = leftKey
      if (!inMainProgram)
      {
        if (speedValue <= minSpd)          // check to see if we're lower than min
        {
          (speedValue = minSpd);          // if so, go no slower
          break;
        }                                  // otherwise:
        speedValue--;                      // decrement the value  
        updateScreen();                    // update the screen
        speedSet();                        // slow down
        }
      break;

    case selectKey:                        // if keyEvent = selectKey then we're starting or stopping
      lcd.clear();                         // clear screen
      lcd.setCursor(0, 1);                  
      if (lastEvent != 0)                  // if we haven't started yet
      {
        lcd.print("    Starting    ");      // print starting
        delay(2000);                        // wait 2 seconds
        speedValue++;                        // set the belt to a slow value
        speedSet();                          // start it moving
        updateScreen();                      // show the main screen
        inMainProgram = false;              // allow the rest of buttons to operate
        lastEvent = 0;                      // next time we press 'select' we're stopping
        break;
      }
      if (lastEvent = 0);
      {
        lcd.print("    Stopping   ");
        speedValue = minSpd;
        inclineValue = minIncl;
        speedSet();
        inclineDown();
        delay(2000);
        updateScreen();
        inMainProgram = true;
        lastEvent = -1;
        break;
      }
      break;
    }
  }
}


// ===================================================================
// I/O control

int speedOut = 0;
int increment = 25.5;
int valueIncl = 0;
int inclineVal = 0; 

void speedSet()
{
  speedOut = speedValue*increment;
  analogWrite(speedPin, speedOut);
}

void inclineUp()
{
  digitalWrite(inclineUpPin, HIGH);
  delay(inclineOutTime);
  digitalWrite(inclineUpPin, LOW);
}


void inclineDown()
{
  digitalWrite(inclineDownPin, HIGH);
  delay(inclineOutTime);
  digitalWrite(inclineDownPin, LOW);
}

// ===================================================================
// Convert ADC value to key number

int adc_key_val[NUM_KEYS] ={ 
  50, 195, 380, 555, 790 };      // NUM_KEYS = 5 (from #define at prog beginning)
int get_key(unsigned int input)	 // get_key value comes from detectKey() below
{
  int k;
  for (k = 0; k < NUM_KEYS; k++) //start counting from 0 to k
  {
    if (input < adc_key_val[k])	// if input is less than adc_key_val[k gets NUM_KEYS value]
        return k;		// then return new value of k
  }
  if (k >= NUM_KEYS)		// if k is >= NUM_KEYS value
    k = -1;                     // then k = -1
  return k;			// return current value of k
}

// ===================================================================
// new key detection routine, without delays!

int     lastKeyEvent = 0;
int     curKeyEvent  = 0;
int     keyToReturn  = 0;
boolean keyToProcess = false;
int     adc_key_in   = 0;

int detectKey()
{
  keyToReturn = -1;			// set keyToReturn as invalid to start this loop
  adc_key_in = analogRead(0);         	// read the value from the sensor  
  curKeyEvent = get_key(adc_key_in);	// convert into key press (send sensor value to 
                                        // get_key function and return a number from 0 to 4)
  if (curKeyEvent != lastKeyEvent)	// if curKeyEvent has changed then:
  {
    if (!keyToProcess)			// if keyToProcess is false (a new key was pressed):
    {
      lastKeyEvent = curKeyEvent;	// update value of lastKeyEvent
      keyToProcess = true;		// and set keyToProcess to true
    }
    else
    {
      keyToReturn = lastKeyEvent;	// otherwise, no new key was pressed
      lastKeyEvent = -1;		// set lastKeyEvent to invalid
      keyToProcess = false;		// and nothing changes
    }
  }
  return keyToReturn;			// return new value
}

// ===================================================================
int inclineValue = 0;		// set initial incline to 1 (flat)

It’s always nice when the comments and the code agree.

  if (keyEvent >= 0)		// if the value of keyEvent is >= 0 then decide what to do:
  {

This is unnecessary. Simply don’t include cases in the switch statement for values below 0.

        if (inclineValue >= maxIncl)  // check we haven't exceeded max incline
        {
          (inclineValue = maxIncl);   // if we have then we can't go further
          break;
        }                              // otherwise:
        inclineValue++;               // increment the value
        updateScreen();               // update the screen with the new value
        inclineUp();                  // and move the treadmill up

This would make more sense like this:

        if (inclineValue < maxIncl)  // If not at the limit
        {
            inclineValue++;               // increment the value
            updateScreen();               // update the screen with the new value
            inclineUp();                  // and move the treadmill up
        }

Most of your comments are useless. Anyone looking at this code will know that inclineValue++ is incrementing the value, and that updateScreen() is updating the screen, and that inclineUp() is going to move the treadmill to a steeper position.

You’ve done a really good job of using meaningful variable names and meaningful function names. Don’t clutter up the code with useless comments.

Or useless parentheses, for that matter. (inclineValue = maxIncl); does not need any parentheses.

      if (lastEvent = 0);

Two problems here. First, you want == to perform a comparison, not = to perform an assignment. The ; on the end forms the body of the statement, and is equivalent in this case to a no-op (do nothing). If you are going to do nothing if the statement is true, and nothing if the statement if false, why have the statement there?

      if (lastEvent != 0)                  // if we haven't started yet
      {
        lcd.print("    Starting    ");      // print starting
// snipped some code
        lastEvent = 0;                      // next time we press 'select' we're stopping
        break;
      }
      if (lastEvent = 0);
      {
        lcd.print("    Stopping   ");

So, if lastEvent is not 0, print “Starting” and set it to 0. Then, if lastEvent is 0 (which it will be), print “Stopping”.

If lastEvent is 0 when the case started, print “Stopping”.

Got it. When the select key is pressed, STOP!

Or was that not your intent?

If you fix these issues, and still have a problem, please come back and post your new code.

Hi PaulS,

Thanks for your input. I needed the fresh (and experienced) eyes to help me see some of the problems. Sorry for all the commenting - I do that for me, since programming is NOT my forte (as you could tell), it helps me 'talk through' what the code is doing.

Your suggestion about the syntax of the case statements - eliminating the code that I thought was 'trapping' the problem of going over or under the min/max and changing around the greater than/less than symbols - is what fixed the problem I was having.

Thanks!!

I have a treadmill w/ a bad upper display unit too. Can you tell me the model number, and how you tied it in to the motor controller board?

Thanks.