Hi,
I'm new to this and has run into a problem with the interrupt function that I cannot figure out. I have 2 limit switches to stop the stepper at the end of travel, one for HOMING and the other for MAX travel. The interrupt function (FALLING) seems to work fine when I test the limits by manually pressing and releasing it.
The problem comes when I power up the stepper driver; the interrupt function still stops the motor when it makes (closes) the limit but when I send a command to move away from the limit (limit open) then it appears as if it sees a FALLING interrupt pin and stop the stepper as soon as the contact open on the limit switch.
I have tried everything, debouncing, changing limit switches and RC circuits but the problem persists.
Could you please look at the code and tell me if there is something obvious wrong.
#include <math.h>
#include <AccelStepper.h>
//User-defined values
long receivedSteps = 0; //Number of steps
long receivedSpeed = 0; //Steps / second
long receivedAcceleration = 0; //Steps / second^2
char receivedCommand; // character, speed, steps etc.
long receivedMMdistance = 0; //distance in mm from the Serial Monitor
long receivedDelay = 0; //delay between two steps, received from the Serial Monitor
long receivedDistance = 0; // user desired distance (mm)
long receivedPosition = 0; // user defined position on axis (mm)
const double screwPitch = 1.5; // Drive screw pitch (mm)- change to 1500micron for better accuracy
const int stepPerRev = 400;
const int defaultMaxSpeed = 400; // set maximum speed for HOMING purposes - steps/s
//-------------------------------------------------------------------------------
int directionMultiplier = 1; // = 1: positive direction, = -1: negative direction
bool newData, runallowed = false; // booleans for new data from serial, and runallowed flag
const byte ledPin = 13; // led status pin, visual feedback from limit switch activation
const byte interruptPin_1 = 2; // pin for the homing microswitch using attachInterrupt(); - travel away from switch (POS)
const byte interruptPin_2 = 3; // pin for the end stop microswitch using attachInterrupt(); - travel away from switch (NEG)
// Test to see if I can get a better debounce response
long debouncing_time = 15; //Debouncing Time in Milliseconds
volatile unsigned long last_micros;
AccelStepper stepper(1, 8, 9);// direction Digital 9 (CCW), pulses Digital 8 (CLK)
void setup()
{
Serial.begin(9600); //define baud rate
Serial.println(" - Demonstration of a 1-D axis using the AccelStepper Library and attacheInterrupt() - \n"); //print a message
Serial.println("Default speed: 400 steps/s, default acceleration: 1000 steps/s^2. \n");
Serial.println("Send 'C' for printing the commands. \n");
pinMode(interruptPin_1, INPUT_PULLUP); /* internal pullup resistor (debouncing); if you choose FALLING, make sure that the switch
connects the pin 2 to the GND when it is pressed. */
attachInterrupt(digitalPinToInterrupt(interruptPin_1), debounceInterrupt, FALLING); /* /You can change FALLING but make sure that you connect
the switch to GND or +5V accordingly! */
pinMode(interruptPin_2, INPUT_PULLUP); /* internal pullup resistor (debouncing); if you choose FALLING, make sure that the switch
connects the pin 3 to the GND when it is pressed. */
attachInterrupt(digitalPinToInterrupt(interruptPin_2), debounceInterrupt, FALLING); /* /You can change FALLING but make sure that you connect
the switch to GND or +5V accordingly! */
//LED pins, OFF by default
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
//setting up some default values for maximum speed and maximum acceleration
stepper.setMaxSpeed(2500); // SPEED = Steps / second
stepper.setAcceleration(1000); // ACCELERATION = Steps /(second)^2
stepper.disableOutputs(); //disable outputs; so the motor does not get warm (no current)
}
// ------------------------------- Main LOOP ----------------------------------------------------------------
void loop()
{
//Constantly looping through these 2 functions.
//We only use non-blocking commands, so something else (should also be non-blocking) can be done during the movement of the motor
checkSerial(); //check serial port for new commands
RunTheMotor(); //function to handle the motor
}
// ------------------------------- Run Motor --------------------------------------------------------------------
void RunTheMotor() //function for the motor
{
if (runallowed == true)
{
// if (abs(stepper.currentPosition()) < receivedSteps) //abs() is needed because of the '<'
// if (stepper.currentPosition() != receivedSteps) // confirm new position is not on current position
stepper.enableOutputs(); //enable pins
stepper.run(); //step the motor (this will step the motor by 1 step at each loop)
}
else // program enters this part if the runallowed is FALSE i.e HOME or Distance travelled, don't do anything
{
runallowed = false; //disable running -> the program will not try to enter this if-else anymore
stepper.disableOutputs(); //disable outputs
}
}
// ----------------------------------- Check Serial - Receive Commands ------------------------------------------
void checkSerial() //function for receiving the commands
{
if (Serial.available() > 0) //if something comes from the computer
{
receivedCommand = Serial.read(); // pass the value to the receivedCommad variable
newData = true; //indicate that there is a new data by setting this bool to true
if (newData == true) //we only enter this long switch-case statement if there is a new command from the computer
{
switch (receivedCommand) //we check what is the command
{
case 'P': /* P uses the move() function of the AccelStepper library, which means that it moves relatively to the current position.
Movement is in the POSITIVE direction */
if (digitalRead(interruptPin_2) != LOW ) /* && runallowed == false) // check if end stop limit switch is not activated */
{
receivedDistance = Serial.parseFloat(); //value for the steps
receivedSpeed = Serial.parseFloat(); //value for the speed
directionMultiplier = 1; //We define the direction
Serial.println("Positive Direction Movement: \n"); //print the action
RotateRelative(); //Run the function
//example: P2000 400 - 2000 steps (5 revolution with 400 step/rev microstepping) and 400 steps/s speed
//In theory, this movement should take 5 seconds
}
else
{
Serial.println("\n Limit Switch Activated! - Unable to move in POSITIVE direction \n"); // Inform user that tbe limit switch has been activated
}
break;
case 'N': /* N uses the move() function of the AccelStepper library, which means that it moves relatively to the current position.
Movement is in the NEGATIVE direction */
if (digitalRead(interruptPin_1) != LOW) // && runallowed == false) // check if end stop limit switch is not activated
{
receivedDistance = Serial.parseFloat(); //value for the steps
receivedSpeed = Serial.parseFloat(); //value for the speed
directionMultiplier = -1; //We define the direction
Serial.println("\n Negative Direction Movement: \n"); //print action
RotateRelative(); //Run the function
//example: N2000 400 - 2000 steps (5 revolution with 400 step/rev microstepping) and 500 steps/s speed; will rotate in the other direction
//In theory, this movement should take 5 seconds
}
else
{
Serial.println("\n Limit Switch Activated! - Unable to move in NEGATIVE direction \n"); // Inform user that tbe limit switch has been activated
}
break;
case 'R': //R uses the moveTo() function of the AccelStepper library, which means that it moves absolute to the current HOME position.
if (digitalRead(interruptPin_2) != LOW ) // check if end stop limit switch is not activated
{
receivedPosition = Serial.parseFloat(); //value for the steps
receivedSpeed = Serial.parseFloat(); //value for the speed
directionMultiplier = 1; //We define the direction
Serial.println("Absolute position (+)."); //print the action
RotateAbsolute(); //Run the function
//example: R800 400 - It moves to the position which is located at +800 steps away from 0.
}
else
{
Serial.println("\n Limit Switch Activated! - Unable to move to specified ABSOLUTE position \n"); // Inform user that tbe limit switch has been activated
}
break;
case 'r': //r uses the moveTo() function of the AccelStepper library, which means that it moves absolute to the current HOME position.
if (digitalRead(interruptPin_1) != LOW) // check if end stop limit switch is not activated
{
receivedPosition = Serial.parseFloat(); //value for the steps
receivedSpeed = Serial.parseFloat(); //value for the speed
directionMultiplier = -1; //We define the direction
Serial.println("Absolute position (-)."); //print the action
RotateAbsolute(); //Run the function
//example: r800 400 - It moves to the position which is located at -800 steps away from 0.
}
else
{
Serial.println("\n Limit Switch Activated! - Unable to move to the specified ABSOLUTE position \n"); // Inform user that tbe limit switch has been activated
}
break;
case 'S': // Stops the motor
stepper.stop(); //stop motor
stepper.disableOutputs(); //disable power
Serial.println("Stop command received! \n"); //print action
runallowed = false; //disable running
break;
case 's': // Stops the motor
stepper.stop(); //stop motor
stepper.disableOutputs(); //disable power
Serial.println("Stop command received! \n"); //print action
runallowed = false; //disable running
break;
case 'A': // Updates acceleration
runallowed = false; //we still keep running disabled, since we just update a variable
stepper.disableOutputs(); //disable power
receivedAcceleration = Serial.parseFloat(); //receive the acceleration from serial
stepper.setAcceleration(receivedAcceleration); //update the value of the variable
Serial.print("New acceleration value: "); //confirm update by message
Serial.println(receivedAcceleration); //confirm update by message
break;
case 'L': //L: Location
runallowed = false; //we still keep running disabled
stepper.disableOutputs(); //disable power
Serial.print("Current location of the motor: ");//Print the message
Serial.println(stepper.currentPosition()); //Printing the current position in steps.
break;
case 'H': //H: Re-establish the HOME position
runallowed = true;
Serial.println("HOMING"); //Print the message
Homing();// Run the HOMING function
break;
case 'h': //h: Go to HOME position (could be different from H: Homing, if changed by USER
runallowed = true;
Serial.println("Go Home"); //Print the message
GoHome();// Run the function
break;
case 'U': // New home position
runallowed = false; //we still keep running disabled
stepper.disableOutputs(); //disable power
stepper.setCurrentPosition(0); //Reset current position. "new home"
Serial.print("The current position is updated to: "); //Print message
Serial.println(stepper.currentPosition()); //Check position after reset.
break;
case 'I': // Check Status of the interrupt pins
Serial.print("Interrupt Pin 1 Status: ");
Serial.println(digitalRead(interruptPin_1));
Serial.print("Interrupt Pin 2 Status: ");
Serial.println(digitalRead(interruptPin_2));
break;
case 'C':
PrintCommands(); //Print the commands for controlling the motor
break;
default:
break;
}
}
newData = false; //after we went through the above tasks, newData is set to false again, so we are ready to receive new commands again.
}
}
// -------------------------------------HOME on Limit Switch --------------------------------------------------
void Homing() // HOME the motor on a limit switch
{
stepper.setMaxSpeed(400); //set speed manually to 400. In this project 400 is 400 step/sec = 1 rev/sec.
directionMultiplier = -1; //We define the direction - NEGATIVE towards limit switch #1
// noInterrupts();
if (stepper.currentPosition() == 0 && digitalRead(interruptPin_1) == LOW) // check current position before moving
{
Serial.println("Currently at the HOME position."); // inform user that carrage is at the HOME position
}
else
{
do // run motor continious until it reached a limit switch
{
stepper.enableOutputs(); //enable pins
stepper.setSpeed(-400); // set the interval speed for homing (pulses/s)
stepper.runSpeed(); // run the motor, no acceleration
} while (digitalRead(interruptPin_1) == HIGH);
stepper.setCurrentPosition(0); //Reset current position - "default HOME position"
Serial.print("At HOME position: "); // confirm the current position
Serial.println(stepper.currentPosition());
Serial.println();
runallowed = false;
stepper.disableOutputs(); //disable power
}
}
// ----------------- Go to USER Defined HOME ----------------------------------------------------
void GoHome() // Move to a user defined HOME position, could be the DEFAULT HOME position if no USER HOME position has been set
{
stepper.setMaxSpeed(400); //set speed manually to 400. In this project 400 is 400 step/sec = 1 rev/sec.
if (stepper.currentPosition() == 0)
{
Serial.println("Currently at the USER HOME position.");
runallowed = false;
stepper.disableOutputs(); //disable power
}
else
{
stepper.enableOutputs(); //enable pins
stepper.setSpeed(400);
stepper.moveTo(0); //set abolute distance to move
}
}
// ------------------------------ Relative MOvement -----------------------------------------
void RotateRelative()
{
//We move X steps from the current position of the stepper motor in a given direction.
//The direction is determined by the multiplier (+1 or -1)
runallowed = true; //allow running - this allows entering the RunTheMotor() function.
receivedSteps = directionMultiplier * ((stepPerRev * receivedDistance) / screwPitch); // Calculate number of steps to complete the distance
stepper.setMaxSpeed(receivedSpeed); //set speed (pulses / sec)
stepper.move(receivedSteps); // set relative distance and direction
}
// ------------------------------ Absalute MOvement -----------------------------------------
void RotateAbsolute()
{
//We move to an absolute position - relative to HOME location.
//The AccelStepper library keeps track of the position.
//The direction is determined by the multiplier (+1 or -1)
//Why do we need negative numbers? - If you drive a threaded rod and the zero position is in the middle of the rod...
runallowed = true; //allow running - this allows entering the RunTheMotor() function.
if (receivedPosition == stepper.currentPosition())
{
Serial.println("Required position is the same as current position! \n");
}
else
{
receivedSteps = ((stepPerRev * receivedPosition) / screwPitch); // Calculate number of steps to complete the distance
stepper.setMaxSpeed(receivedSpeed); //set speed (pulses / sec)
stepper.moveTo(directionMultiplier * receivedSteps); //set relative distance
}
}
// --------------------------- Interrupt Debouncing -----------------------------------------
// Test to improve debouncing limit switches
void debounceInterrupt() {
Serial.print("interruptPin_1 Status (bebounce): "); // Output for debugging
Serial.println(digitalRead(interruptPin_1));
Serial.print("interruptPin_2 Status (bebounce): "); // Output for debugging
Serial.println(digitalRead(interruptPin_2));
Serial.println("");
if ((long)(micros() - last_micros) >= debouncing_time * 1000)
{
stopMotor();
last_micros = micros();
}
}
// -------------------------- STOP MOTOR ------------------------------------------------------
void stopMotor()//function activated by the pressed microswitch or E-stop command
{
//Stop motor, disable outputs; here we should also reset the numbers if there are any
Serial.print("interruptPin_1 Status (Stop Motor): "); // use for debugging
Serial.println(digitalRead(interruptPin_1)); // use for debugging
Serial.print("interruptPin_2 Status (Stop Motor): "); // use for debugging
Serial.println(digitalRead(interruptPin_2)); // use for debugging
if (digitalRead(interruptPin_1) == 0)
{
runallowed = false; // disable running
stepper.stop(); //stop motor
Serial.println("MOTOR STOPPED! \n"); // print action
stepper.disableOutputs(); //disable power
}
else if (digitalRead(interruptPin_2) == 0)
{
runallowed = false; // disable running
stepper.stop(); //stop motor
Serial.println("MOTOR STOPPED! \n"); // print action
stepper.disableOutputs(); //disable power
}
else
{
/*Serial.print("interruptPin_1 Status (Stop Motor else:) "); // output for debugging
Serial.println(digitalRead(interruptPin_1));
Serial.print("interruptPin_2 Status: ");
Serial.println(digitalRead(interruptPin_2)); */
}
}
// ------------------------------- Print Commands --------------------------------------------
void PrintCommands()
{
//Printing the commands
Serial.println(" 'C' : Prints all the commands and their functions.");
Serial.println(" 'P' : Rotates the motor in positive (CW) direction, relative.");
Serial.println(" 'N' : Rotates the motor in negative (CCW) direction, relative.");
Serial.println(" 'R' : Rotates the motor to an absolute positive position (+).");
Serial.println(" 'r' : Rotates the motor to an absolute negative position (-).");
Serial.println(" 'S' : Stops the motor immediately.");
Serial.println(" 's' : Stops the motor immediately.");
Serial.println(" 'A' : Sets an acceleration value.");
Serial.println(" 'L' : Prints the current position/location of the motor.");
Serial.println(" 'H' : Goes back to 0 position from the current position (homing).");
Serial.println(" 'U' : Updates the position current position and makes it as the new 0 position. ");
Serial.println(" 'I' : Check the status of the interrupt pins. ");
}