Programming stepper motors to move until limit switches

Hi I am working on a project which will be using some stepper motors to control dampers opening and closing on a linkage system. For this, we are using Automation Direct Stepper motors and drivers and a ClearCore board.

I am trying to get both stepper motors to drive in one direction, and then have each motor stop when a limit switch is achieved for that individual motor. Once both limit switches have been achieved, I need the motors to reverse and drive until they each hit a second limit switch, then start a timer and repeat.

I have gotten to a point where it will sometimes work, but I have not been able to get it to work consistently. Sometimes it will blow right by the limit switch sensor and continue rotating. For our application it can only do a 180* Turn then needs to reverse directions.

I was hoping someone may be able to point me into the correct direction to get this to work consistently. Thank you in advance


#include "ClearCore.h"
#include "DigitalIn.h"
#include <DigitalInAnalogIn.h>

// Specifies which motor to move.
// Options are: ConnectorM0, ConnectorM1, ConnectorM2, or ConnectorM3.
#define motor1 ConnectorM0
#define motor2 ConnectorM1

#define inputPin6 DI6
#define inputPin7 DI7
#define inputPin1 IO1
#define inputPin0 IO0
#define OnSwitch A12
// The current state of the input pin
PinStatus state;

bool SensorStatus0;
bool SensorStatus1;
bool SensorStatus6;
bool SensorStatus7;
bool SwitchState;



// Select the baud rate to match the target serial device
#define baudRate 9600

// This example has built-in functionality to automatically clear motor alerts, 
//	including motor shutdowns. Any uncleared alert will cancel and disallow motion.
// WARNING: enabling automatic alert handling will clear alerts immediately when 
//	encountered and return a motor to a state in which motion is allowed. Before 
//	enabling this functionality, be sure to understand this behavior and ensure 
//	your system will not enter an unsafe state. 
// To enable automatic alert handling, #define HANDLE_ALERTS (1)
// To disable automatic alert handling, #define HANDLE_ALERTS (0)
#define HANDLE_ALERTS (1)

// Define the velocity and acceleration limits to be used for each move
int accelerationLimit = 1000; // pulses per sec^2

// Declares user-defined helper functions.
// The definition/implementations of these functions are at the bottom of the sketch.
bool MoveAtVelocity(int32_t velocity);
void PrintAlerts();
void HandleAlerts();

void setup() {
    // Put your setup code here, it will only run once:
ConnectorA12.Mode(Connector::INPUT_ANALOG);

    pinMode(inputPin0, INPUT); // Set the pin as an INPUT
    pinMode(inputPin1, INPUT);
    pinMode(inputPin6, INPUT); // Set the pin as an INPUT
    pinMode(inputPin7, INPUT);
    pinMode(OnSwitch, INPUT);

    

    // Sets the input clocking rate. This normal rate is ideal for ClearPath
    // step and direction applications.
    MotorMgr.MotorInputClocking(MotorManager::CLOCK_RATE_NORMAL);

    // Sets all motor connectors into step and direction mode.
    MotorMgr.MotorModeSet(MotorManager::MOTOR_ALL,
                          Connector::CPM_MODE_STEP_AND_DIR);
    motor1.PolarityInvertSDEnable(true);
    motor1.PolarityInvertSDDirection(true);
    motor1.PolarityInvertSDHlfb(true);
    motor2.PolarityInvertSDEnable(true);
    motor2.PolarityInvertSDDirection(true);
    motor2.PolarityInvertSDHlfb(true);
    // Set the motor's HLFB mode to bipolar PWM
    motor1.HlfbMode(MotorDriver::HLFB_MODE_HAS_BIPOLAR_PWM);
    // Set the HFLB carrier frequency to 482 Hz
    motor1.HlfbCarrier(MotorDriver::HLFB_CARRIER_482_HZ);

    // Set the maximum acceleration for each move
    motor1.AccelMax(accelerationLimit);
    motor2.HlfbMode(MotorDriver::HLFB_MODE_HAS_BIPOLAR_PWM);
    // Set the HFLB carrier frequency to 482 Hz
    motor2.HlfbCarrier(MotorDriver::HLFB_CARRIER_482_HZ);

    // Set the maximum acceleration for each move
    motor2.AccelMax(accelerationLimit);

    // Sets up serial communication and waits up to 5 seconds for a port to open.
    // Serial communication is not required for this example to run.
    Serial.begin(baudRate);
    uint32_t timeout = 5000;
    uint32_t startTime = millis();
    while (!Serial && millis() - startTime < timeout) {
        continue;
    }

    // Enables the motor; homing will begin automatically if enabled
    motor1.EnableRequest(true);
    Serial.println("Motor Enabled");
 motor2.EnableRequest(true);
    Serial.println("Motor Enabled");

    // Waits for HLFB to assert (waits for homing to complete if applicable)
    Serial.println("Waiting for HLFB...");
    while (motor1.HlfbState() != MotorDriver::HLFB_ASSERTED &&
			!motor1.StatusReg().bit.AlertsPresent) {
        continue;
    }
	// Check if motor alert occurred during enabling
	// Clear alert if configured to do so 
    if (motor1.StatusReg().bit.AlertsPresent) {
		Serial.println("Motor alert detected.");		
		PrintAlerts();
		if(HANDLE_ALERTS){
			HandleAlerts();
		} else {
			Serial.println("Enable automatic alert handling by setting HANDLE_ALERTS to 1.");
		}
		Serial.println("Enabling may not have completed as expected. Proceed with caution.");		
 		Serial.println();
	} else {
		Serial.println("Motor Ready");	
	}
}

void loop() {
    // Put your main code here, it will run repeatedly:
float analogReadingV = ConnectorA12.AnalogVoltage();
//Serial.println(analogReadingV);
SwitchState = analogRead(OnSwitch);
delay(1);

 // Serial.write(analogReadingV);
  if (analogReadingV > 1){
  //  if (ConnectorM0.EnableRequest()) {
    // If the motor is currently enabled, disable it.
    ConnectorM0.EnableRequest(false);
//}
//if (ConnectorM1.EnableRequest()) {
    // If the motor is currently enabled, disable it.
    ConnectorM1.EnableRequest(false);
//}
  }
  if (analogReadingV <1) {
 //if (ConnectorM0.EnableRequest()) {
    // If the motor is currently enabled, disable it.
    ConnectorM0.EnableRequest(true);
//}
//if (ConnectorM1.EnableRequest()) {
    // If the motor is currently enabled, disable it.
    ConnectorM1.EnableRequest(true);
//}

    
  }
  /*
  Serial.print("Motors: ");
    if (analogReadingV < 1) {
        Serial.println("ON");
    }
    else {
        Serial.println("OFF");
    }

    // Wait a second thebn repeat...
    delay(100);

    state = digitalRead(inputPin6);

    // Display the state of the input connector.
    Serial.print("DI-6 Input state: ");
    if (state) {
        Serial.println("ON");
    }
    else {
        Serial.println("OFF");
    }

    // Wait a second thebn repeat...
    delay(100);

    state = digitalRead(inputPin7);

    // Display the state of the input connector.
    
    
    Serial.print("DI-7 Input state: ");
        if (state) {
        Serial.println("ON");
    }
    else {
        Serial.println("OFF");
    }
    // Wait a second then repeat...
    delay(100);

     state = digitalRead(inputPin0);

    // Display the state of the input connector.
    Serial.print("IO-0 Input state: ");
    if (state) {
        Serial.println("ON");
    }
    else {
        Serial.println("OFF");
    }
 state = digitalRead(inputPin1);

    // Display the state of the input connector.
    Serial.print("I0-1 Input state: ");
    if (state) {
        Serial.println("ON");
    }
    else {
        Serial.println("OFF");
    }
delay(100);
*/
SensorStatus0 = digitalRead(inputPin0);
SensorStatus1 = digitalRead(inputPin1);
SensorStatus6 = digitalRead(inputPin6);
SensorStatus7 = digitalRead(inputPin7);


 if (SensorStatus6 == false && (motor2.MoveVelocity(-100))){

motor2.MoveVelocity(0);

}
delay(10);

if (SensorStatus7 == false && (motor1.MoveVelocity(-100))){


motor1.MoveVelocity(0);
  
}
delay(10);

if (SensorStatus0 == false && (motor1.MoveVelocity(100))){

motor2.MoveVelocity(0);

}
delay(10);

if (SensorStatus1 == false && (motor2.MoveVelocity(100))){


motor1.MoveVelocity(0);
  
}
delay(10);

if (SensorStatus6 == false && SensorStatus7 == false)

{
motor1.MoveVelocity(0);
motor2.MoveVelocity(0);
delay(2000);

motor1.MoveVelocity(100);
motor2.MoveVelocity(100);
delay(200);

motor1.MoveVelocity(100);
motor2.MoveVelocity(100);

  if (SensorStatus1 == false)
  {
    motor2.MoveVelocity(0);
  }
  delay(1);

    if (SensorStatus0 == false)
  {
    motor1.MoveVelocity(0);
  } 
delay(1);
} 
delay(100);

if(SensorStatus0 == false && SensorStatus1 == false)

{
motor1.MoveVelocity(0);
motor2.MoveVelocity(0);
delay(2000);

motor1.MoveVelocity(-100);
motor2.MoveVelocity(-100);
delay(200);


motor1.MoveVelocity(-100);
motor2.MoveVelocity(-100);

  if (SensorStatus6 == false)
  {
    motor2.MoveVelocity(0);
  }
  delay(1);

    if (SensorStatus7 == false)
  {
    motor1.MoveVelocity(0);
  }
  delay(1);
}
delay(100);
/*
if (SensorStatus1 == false) {
 // Move at 1,000 steps/sec for 2000ms
    MoveAtVelocity(100);
    delay(5000);

MoveAtVelocity(0);
delay(5000);
    // Move at -5,000 steps/sec for 2000ms
    MoveAtVelocity(-100);
    delay(5000);
 MoveAtVelocity(0);  
 delay(10000);
    }
  */ 
}
/*------------------------------------------------------------------------------
 * MoveAtVelocity
 *
 *    Command the motor to move at the specified "velocity", in pulses/second.
 *    Prints the move status to the USB serial port
 *
 * Parameters:
 *    int velocity  - The velocity, in step pulses/sec, to command
 *
 * Returns: True/False depending on whether the move was successfully triggered.
 */
bool MoveAtVelocity(int velocity) {
    // Check if a motor alert is currently preventing motion
	// Clear alert if configured to do so 
    if (motor1.StatusReg().bit.AlertsPresent) {
		Serial.println("Motor alert detected.");		
		PrintAlerts();
		if(HANDLE_ALERTS){
			HandleAlerts();
		} else {
			Serial.println("Enable automatic alert handling by setting HANDLE_ALERTS to 1.");
		}
		Serial.println("Move canceled.");		
		Serial.println();
        return false;
    }

    Serial.print("Moving at velocity: ");
    Serial.println(velocity);

    // Command the velocity move
    motor1.MoveVelocity(velocity);
    motor2.MoveVelocity(velocity);
    // Waits for the step command to ramp up/down to the commanded velocity. 
    // This time will depend on your Acceleration Limit.
    Serial.println("Ramping to speed...");
    while (!motor1.StatusReg().bit.AtTargetVelocity) {
        continue;
    }

    Serial.println("At Speed");
    return true; 
}
//------------------------------------------------------------------------------


/*------------------------------------------------------------------------------
 * PrintAlerts
 *
 *    Prints active alerts.
 *
 * Parameters:
 *    requires "motor" to be defined as a ClearCore motor connector
 *
 * Returns: 
 *    none
 */
 void PrintAlerts(){
	// report status of alerts
 	Serial.println("Alerts present: ");
	if(motor1.AlertReg().bit.MotionCanceledInAlert){
		Serial.println("    MotionCanceledInAlert "); }
	if(motor1.AlertReg().bit.MotionCanceledPositiveLimit){
		Serial.println("    MotionCanceledPositiveLimit "); }
	if(motor1.AlertReg().bit.MotionCanceledNegativeLimit){
		Serial.println("    MotionCanceledNegativeLimit "); }
	if(motor1.AlertReg().bit.MotionCanceledSensorEStop){
		Serial.println("    MotionCanceledSensorEStop "); }
	if(motor1.AlertReg().bit.MotionCanceledMotorDisabled){
		Serial.println("    MotionCanceledMotorDisabled "); }
	if(motor1.AlertReg().bit.MotorFaulted){
		Serial.println("    MotorFaulted ");
	}
 }
//------------------------------------------------------------------------------


/*------------------------------------------------------------------------------
 * HandleAlerts
 *
 *    Clears alerts, including motor faults. 
 *    Faults are cleared by cycling enable to the motor.
 *    Alerts are cleared by clearing the ClearCore alert register directly.
 *
 * Parameters:
 *    requires "motor" to be defined as a ClearCore motor connector
 *
 * Returns: 
 *    none
 */
 void HandleAlerts(){
	if(motor1.AlertReg().bit.MotorFaulted){
		// if a motor fault is present, clear it by cycling enable
		Serial.println("Faults present. Cycling enable signal to motor to clear faults.");
		motor1.EnableRequest(false);
		Delay_ms(10);
		motor1.EnableRequest(true);
	}
	// clear alerts
	Serial.println("Clearing alerts.");
	motor1.ClearAlerts();
 }
//------------------------------------------------------------------------------

 

The "homing" approach that most people take, which is very reliable, can be outlined like this.
The code assumes that an open switch is read as HIGH.

while (digitalRead(switchpin) == HIGH) take_one_step();

Of course, nothing can happen during delay(), and for responsive programming, you should learn how to remove most, if not all of those calls to delay().

2 Likes

When using "deceleration" the motor does not stop instantly but slows down and then stops. Is that causing the motor to overrun the limit switch?

1 Like

Are these all wired so their normal condition is pulled high or is it pulled low?

These are all normally closed so their default state is pulled high

I did not consider that but based on the fact it is able to run right over the sensor without ever appearing to stop that could be a possibility. Is there a way to code in a "hard stop" to ensure the motors stop as soon as the sensor is triggered? I have started trying to write a version which would use interrupts to stop the motors outside of the loop in hopes that would have a quicker response time,

I will try to add this in thank you.

I was trying to remove as many of the delays as possible, but am having issues with getting the sensors to 'continually' check the sensor status while in the loop. That is why some of those have small delays- simply because I was trying to get them to check and update more often. How would you go about trying to remove some more of these?

What sort of issues?

What does adding a delay accomplish?

How are they wired?

Do you have a pulldown resistor (about 10k) connected between input pin and GND?

Are your switches wired like any above?

I've done this by using an AND gate and basically switch off the STEP signal when a switch is triggered. Now you also have to use another AND port to allow the STEP to go through again when the DIR signal is reversed, so at least you can always move back out of the triggered switch.

That was a safety backup - in case the code didn't catch the end stop for whatever reason while the stepper continued to run.

The basic solution to your problem is as given in #2. Take one step, test condition. Take another step, test condition. Until the condition is met (switch triggered). Your stepper library may be taking a lot of steps before returning, not allowing you to check in between.

Another thing: I notice you have numbered variables (inputPin0, inputPin1, ...). You should look into using arrays for those things, it can make your life a lot easier and your code simpler and easier to maintain.

Is the limit switch momentary, i.e. once past the limit switch it stops triggering?

Either way I think your approach is problematic. Steppers motors are inherently open loop, you need to add some closed loop behaviour. This is easier if you are controlling the motors directly, but in this case it appears you are setting a velocity, and some other part is actually generating the steps.

Your current code might work if you either poll the limit switches constantly, or better use an interrupt. When the switch is triggered, change the velocity direction. There may still be an issue with deceleration overshooting the trigger. Possibly that could be solved by moving the limit switch?

However, the ClearCore library appears to have support for limit switches. Once configured, it will automatically stop motion when the trigger is reached, which appears to be what you want. You would then need to poll the motor interface to see if motion has stopped, or perhaps there is an alert.

There are other possibilities, but using the builtin limit switch support might be ok.

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