Fooling device into thinking Arduino has restarted

Working on a project to control a MC-2100 treadmill motor controller with a Uno.
This requires a 20hz PWM signal that We have working beautifully.

The problem occurs after the duty cycle has been set to anything above 0 and then returned to 0 the MC-2100 will not respond to any further input until the Arduino has been restarted.
Research has shown this is a safety feature of the MC-2100 called "soft start" that prevents the treadmill from restarting at a previously set speed. This issue is described in this video:

In the video he is using one of those cheap function generators to generate the signal to control the treadmill motor. To get around the soft start feature he simply turns off/on the function generator to get it to work again. Note that Power to the MC-2100 is not cycled only power to the function generator is cycled.

In my code I use the reset function to restart the Arduino and that does work, but I would like to find a solution that doesn't require a full reset. Since the only connection's between the 2 are 12v Power and ground from the MC-2100 thru a buck converter set at 7v powering the Arduino. and the PWM wire. I thought that setting the pinMode to INPUT might fool the MC-2100 into thinking it had been reset. But this did not work.

Is there a way to fool the MC-2100 into thinking the Arduino was reset without actually resetting it?

Edit: Forgot the code:




// ================= LCD ================
//  https://github.com/duinoWitchery/hd44780

#include <Wire.h>
#include <hd44780.h>                        // main hd44780 header -
#include <hd44780ioClass/hd44780_I2Cexp.h>  // i2c expander i/o class header

// declare lcd object: auto locate & auto config expander chip
hd44780_I2Cexp lcd;

static constexpr uint8_t LCD_ROWS = 4;
static constexpr uint8_t LCD_COLS = 20;


// ================= KEYPAD ================

#include <Keypad.h>

const byte ROW_NUM = 4;
const byte COLUMN_NUM = 4;
char keys[ROW_NUM][COLUMN_NUM] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};
byte pin_rows[ROW_NUM] = { 13, 12, 11, 9 };
byte pin_column[COLUMN_NUM] = { 8, 7, 6, 5 };
Keypad keypad = Keypad(makeKeymap(keys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM);


// ================= CONSTANTS ================
constexpr uint8_t PWM_PIN = 10;
constexpr int TACHO_PIN = 2;         // Constants should be all capitol letter's <<<<<
constexpr uint16_t MAX_DUTY = 5300;  // 85% of TOP_COUNT

// ================= VARIABLES ================

uint16_t maxTargetDuty = 1000;
uint16_t targetDuty = 0;
uint16_t oldTargetDuty = 0;

int rpm;

// ================= Millis() Timing VARIABLES ================

unsigned long currentTime = 0;         // can be used for several timers update it every loop
unsigned long displayStartTime = 0;    // used to start/restart the timer
unsigned long displayInterval = 1000;  // How often to update the display

unsigned long tachoStartTime = 0;
unsigned long tachoInterval = 1000;

// ================= Interrupt Service Routine For Tacho ================
volatile unsigned long counter = 0;  // Counter variable for revolutions

void IRS_RpmCounter() {
  counter++;
}

void(* resetFunc) (void) = 0;//declare reset function at address 0 

// ================================================================
// *                         setup()                              *
// ================================================================

void setup() {

  // Interrupt for Tacho
  attachInterrupt(digitalPinToInterrupt(TACHO_PIN), IRS_RpmCounter, FALLING);

  // Initializes timer 1 to produce a 20 hz pwm on Pin 10
  pinMode(PWM_PIN, OUTPUT);
  init20hzPWM();

  // Initialize Display
  lcd.begin(LCD_COLS, LCD_ROWS);

  lcd.backlight();              // added turn on LCD backlight
  lcd.setCursor(0, 0);          // added move LCD cursor to column 0 row 0
  lcd.print(" Uno Treadmill");  // added display Uno Treadmill on LCD display
  delay(2000);                  // added wait 2 seconds before continuing

}  // End setup()

// ================================================================
// *                          loop()                              *
// ================================================================


void loop() {

  char key = keypad.getKey();


  switch (key) {
    case 'A': // Apply the desired duty cycle to start machine
      pinMode(PWM_PIN, OUTPUT);
      applyDutyCycle();
      break;

    case 'B': // Stop the machine.... currently by restarting the Arduino
      resetFunc(); //call reset

      //targetDuty = 0;
      //applyDutyCycle();
      break;

    case 'C': // Small increase in duty cycle applied immediatly
      targetDuty += 25;
      applyDutyCycle();
      break;

    case 'D': // Small decrease in duty cycle applied immediatly
      targetDuty -= 25;
      applyDutyCycle();
      break;

    default: // Collect input data for duty cycle
      byte numKey = key - '0';
      if (numKey >= 0 && numKey <= 9) {
        targetDuty = targetDuty * 10 + numKey;
      }
      break;
  }

  currentTime = millis();

  updateRPM();

  if (currentTime - displayStartTime >= displayInterval) {
    displayStartTime = currentTime;  // restarts timer
    updateDisplay();
  }

}  // End loop()

// ================================================================
// *                       updateRPM()                       *
// ================================================================

void updateRPM() {

  unsigned long tempCount;

  if (currentTime - tachoStartTime >= tachoInterval) {
    tachoStartTime = currentTime;

    noInterrupts();
    tempCount = counter;
    counter = 0;
    interrupts();

    rpm = tempCount * 60;  // Calculate RPM
  }
}

// ================================================================
// *                       updateDisplay()                        *
// ================================================================

void updateDisplay() {
  lcd.setCursor(0, 0);
  lcd.print("Set Dutycycle: ");
  lcd.print(targetDuty);
  lcd.setCursor(0, 1);
  lcd.print("Current RPM: ");
  lcd.print(rpm);
  lcd.setCursor(0, 2);
  lcd.print("Duty Cycle: ");
  lcd.print(oldTargetDuty);
  lcd.setCursor(0, 3);
  lcd.print("A to start B to stop");
}  // End updateDisplay()

// ================================================================
// *                       applyDutyCycle()                       *
// ================================================================

void applyDutyCycle() {
  // Constrain targetDuty within range
  if (targetDuty > maxTargetDuty) {
    targetDuty = maxTargetDuty;
  }
  oldTargetDuty = targetDuty;

  uint16_t dutyCycle = map(targetDuty, 0, maxTargetDuty, 0, MAX_DUTY);

  static uint16_t oldDutyCycle;
  if (oldDutyCycle != dutyCycle) {
    oldDutyCycle = dutyCycle;

    // setting OCR1x applies dutyCycle
    OCR1B = dutyCycle;
  }
}  // End applyDutyCycle()

// ================================================================
// *                       init20hzPWM()                          *
// *  Set up Timer1 for 20hz phase correct PWM output on pin 10   *
// ================================================================

void init20hzPWM() {

  // Desired Clock Divider Value
  constexpr uint16_t PRESCALER = 64;

  // Desired Frequncy in Herz
  constexpr uint16_t FREQUENCY = 20;

  // Formula to determine the TOP value, which sets the frequency
  // The result must be a unsigned 16 bit integer to be valid (more info needed)
  constexpr uint16_t TOP_COUNT = F_CPU / PRESCALER / FREQUENCY / 2;

  // Set Compare Output Mode(set OC1B(pin 10) on compare match @TOP, clear OC1B @BOTTOM)
  TCCR1A |= (1 << COM1A0);
  TCCR1A |= (1 << COM1B1);

  // Set Prescaler(Clock Divided by 64)
  TCCR1B |= (1 << CS10) | (1 << CS11);

  // Set Waveform Mode(Mode 9 Phase and Frequency correct using OCR1A for TOP)
  TCCR1A |= (1 << WGM10);
  TCCR1B |= (1 << WGM13);

  // Set Frequency (20Hz)
  OCR1A = TOP_COUNT;

  // Set Dutycycle
  OCR1B = 0;
}  // End init20hzPWM()


// End Program

EDIT2: I should add that the Tach is a work in progrees and the tach wire is not currently hooked up.

Since the only way they communicate is by the PWM_PIN, perhaps something like this completely untested function would work as well as disconnecting the wire for 0.5sec:

   void cyclePwmConnection(void){
      pinMode(PWM_PIN,INPUT); 
      delay(500);
      pinMode(PWM_PIN,OUTPUT); 
   }

Oops. I didn't read this bit:

Since just setting the pin to INPUT doesn't seem to work (odd), maybe just reset all the registers you set up so you aren't generating your pulse

byte originalTCCR1A;
byte originalTCCR1B;
...
void setup() {
...
  originalTCCR1A = TCCR1A;
  originalTCCR1B = TCCR1B;
...
}

void loop()
{
...
    case 'B': // Stop the machine.... currently by restarting the Arduino
       TCCR1A = originalTCCR1A;
       TCCR1B = originalTCCR1B;
        pinMode(PWM_PIN, INPUT);
        break;
...

Also, your TACH pin can not be pin 10 - you are already using that and it not an interrupt pin on an UNO.

1 Like

The video mentions time delays on the order of 5-10-15-20 seconds. Have you tried the disconnecting the wire like the NC switch at 3:39 in the video? Two of the devices are pulled low by their pots, and the MC-2100 is open circuited.

image
vs
image

Thanks for the input it is greatly appreciated.

@DaveX
Yeah, even with a ridiculous 30 second delay switching to INPUT had no effect.
Disconnecting the PWM wire for just a split second does work but I'd rather not add another button. Besides I don't have one that is NC.

@blh64
I haven't tried this yet. but I will get it coded and try it when I get back out in the shop tonight and let you know how it goes.
I also was surprised that setting to INPUT did not work.
The Tach has always been on on Pin 2.

Can you check if there's a voltage on the disconnected MC-2100's PWM input pin?

I notice your code instantly changes from one speed to another, have you tried slowly ramping the speed between changes?

Do you have any problems with the motor starting if you set the duty cycle to something well above zero before pressing the 'A' key to start the motor?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.