Ping Pong Oscillation with Optical Endstop

Hello Experts!

I'd like to get help that I know has been addressed in other posts -- but unfortunately, I cannot get it to work in my particular setup after numerous attempts and experimentation. I'd like to use a Lerdge Optical endstop limit as just a trigger to cause the motor to change direction and move away from the switch and keep going until it reaches the other limit switch -- similar to a game of Ping-Pong. The mechanism itself will be a rack and pinion that will trip the sensor at each end.

I'm using an Arduino UNO R3 and a bipolar stepper motor driven by a TB6600 micro step driver. The stepper is powered by an external voltage source.

Here is the code I have so far:

#include <AccelStepper.h>
//accelstepper library

AccelStepper stepper(1, 8, 9);
// direction Digital 9 (CCW), pulses Digital 8 (CLK)

int stepDirection;

int limitswitch1 = 2;    //input switch1; pin 2
int limitswitch2 = 3;    //input switch2; pin 3



void setup()
{
  pinMode(limitswitch1, INPUT_PULLUP);
// internal pullup resistor (debouncing)
  pinMode(limitswitch2, INPUT_PULLUP);
// internal pullup resistor (debouncing)

  stepper.setMaxSpeed(5000); //SPEED = Steps / second //Stepper parameters
  stepper.setAcceleration(1000); //ACCELERATION = Steps /(second)^2
  stepper.setSpeed(1500);
  delay(500);

}


void loop()
{
  if (digitalRead(limitswitch1) == LOW)       //limitswitch1 read 0V; blocked
  {
    stepper.setSpeed(-1500);                  //turn anti-clockwise
  }
  else if (digitalRead(limitswitch2) == LOW)  //limitswitch2 read 0V
  {
    stepper.setSpeed(1500);                   //turn clockwise
  }
  stepper.runSpeed();                         //step the motor (this will step the motor by 1 step at each loop indefinitely)
}

As it stands now, I can get the motor to initially rotate CW. And when I block Limit Switch 1, the motor successfully changes direction to CCW. However, when I unblock the sensor, the motor automatically returns to a CW rotation.

Limit Switch 2 does nothing when blocked as it I assume it would since it is defined as CW.

Can the group please help me achieve this ping-pong actuation?

Thanks in advance!!!

Hi. So I've made some progress. I updated the code and changed the optical endstops to mechanical switches. However, the switching is inconsistent. Sometimes it changes direction, sometimes it doesn't. I've read that it could be a debouncing issue and/or delay insertion solution.

I also put a .1uF cap across the the 5V and ground terminals of the switches.

Could someone please take a look at the code and let me know where is the best place for inserting a delay? I'm so close!!!! I've just tried a scientific method of inserting delays anywhere and seeing what happens. I cannot get a consistent response.

Thanks in advance!

#include <AccelStepper.h> //accelstepper library

int limitswitch1 = 2; //pin for the microswitch using attachInterrupt()
int limitswitch2 = 3; //pin for the microswitch using attachInterrupt()

bool switchFlipped = false; //stores the status for flipping
bool previousFlip = true; //stores the previous state for flipping - needed for the direction change

// direction Digital 9 (CCW), pulses Digital 8 (CLK)
AccelStepper stepper(1, 8, 9);

void setup()
{
  //Limit Switches
  pinMode(limitswitch1, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(limitswitch2, INPUT_PULLUP); // internal pullup resistor (debouncing)

  attachInterrupt(digitalPinToInterrupt(limitswitch1), FlipDirection, FALLING);   //do not change it to 'CHANGE'
  attachInterrupt(digitalPinToInterrupt(limitswitch2), FlipDirection, FALLING);
  //---------------------------------------------------------------------------

  //Serial Communication
  Serial.begin(9600); //defining some baud rate
  Serial.println("Testing Accelstepper"); //print a message
  //---------------------------------------------------------------------------

  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setMaxSpeed(5000); //SPEED = Steps / second
  stepper.setAcceleration(1000); //ACCELERATION = Steps /(second)^2
  stepper.setSpeed(1500);
  delay(500);
  //---------------------------------------------------------------------------

}

void loop()
{

  stepper.runSpeed(); //step the motor (this will step the motor by 1 step at each loop indefinitely)
  flipCheck();   //checking the flip in each loop
}

void flipCheck()
{
  if (switchFlipped == true)
  {

    if (previousFlip == true) //If the previous flip is 1, we have positive direction
    {
      stepper.setSpeed(1500);
      delay(500);
    }
    if (previousFlip == false) //If the previous flip is 0, we have negative direction
    {
      stepper.setSpeed(-1500);
      delay(500);
    }
    switchFlipped = false;
    //We have to reset this, so in the next iteration of the loop, the code will not enter this part, only when there was a click again
  }

}

void FlipDirection()
{
  delay(1000);
  switchFlipped = true; //we change the status to true, so the code will enter the flipCheck() function
  delay(1000);
  previousFlip = !previousFlip; //change the state to different from the previous - this controls the direction

}

Delays are certainly the wrong thing to try. Try putting serial.print messages to trace your logic, instead.

Paul

There are lots of problems with that code, mostly to do with the use of interrupts, which is not necessary.

  1. delay() does not work within an interrupt
  2. variables shared with interrupt routines must be declared volatile
  3. multibyte variables shared with interrupt routines must be protected against corruption

It is best to get rid of the interrupts. Here is how I have a stepper move to an endstop:

while (digitalRead(endstop) == HIGH) take_one_step(); //or LOW, as the switch requires

That is blocking code, of course, so if something else needs to be done while stepping, don't use a while() loop.

Hi Paul and jremington. Thank you for the input. I have removed the delays and the interrupts. As I am quite green with programming, I made an attempt at the while statement. I may need some more pushing along so I understand.

Here is the code I updated. Unfortunately, nothing happens at all now. The motor doesn't even start. Some more suggestions so I can learn?

#include <AccelStepper.h> //accelstepper library

const byte limitSwitch_1 = 2; //pin for the microswitch using attachInterrupt()
const byte limitSwitch_2 = 3; //pin for the microswitch using attachInterrupt()

bool switchFlipped = false; //stores the status for flipping
bool previousFlip = true; //stores the previous state for flipping - needed for the direction change

//------------------------------------------------------------------------------------

// direction Digital 9 (CCW), pulses Digital 8 (CLK)
AccelStepper stepper(1, 8, 9);

void setup()
{
  //Limit Switches
  pinMode(limitSwitch_1, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(limitSwitch_2, INPUT_PULLUP); // internal pullup resistor (debouncing)
  //---------------------------------------------------------------------------

  //Serial communication
  Serial.begin(9600); //defining some baud rate
  Serial.println("Testing Accelstepper"); //print a message
  //---------------------------------------------------------------------------

  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setMaxSpeed(5000); //SPEED = Steps / second
  stepper.setAcceleration(1000); //ACCELERATION = Steps /(second)^2
  stepper.setSpeed(2000);
  //---------------------------------------------------------------------------

}

void loop() //this function constantly polls the pins. It is not the most efficient practice...
{
  while (digitalRead(limitSwitch_1) == HIGH)
  {
    stepper.runSpeed(); //step the motor (this will step the motor by 1 step
  }
}

Ok. Quick update. My power source wiring was faulty, and I have since fixed that. I also successfully updated the suggested while code such that the Sensor was LOW. And thing are back up and running!!!!!

So the motor spins CCW. When I block the optical switch, the motor stoops. Can I get some help in getting the motor to then reverse direction?

Thanks! This is very exciting to see progress. I appreciate the help.

#include <AccelStepper.h> //accelstepper library

const byte limitSwitch_1 = 2; //pin for the microswitch using attachInterrupt()
const byte limitSwitch_2 = 3; //pin for the microswitch using attachInterrupt()

bool switchFlipped = false; //stores the status for flipping
bool previousFlip = true; //stores the previous state for flipping - needed for the direction change

//------------------------------------------------------------------------------------

// direction Digital 9 (CCW), pulses Digital 8 (CLK)
AccelStepper stepper(1, 8, 9);

void setup()
{
  //Limit Switches
  pinMode(limitSwitch_1, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(limitSwitch_2, INPUT_PULLUP); // internal pullup resistor (debouncing)
  //---------------------------------------------------------------------------

  //Serial communication
  Serial.begin(9600); //defining some baud rate
  Serial.println("Testing Accelstepper"); //print a message
  //---------------------------------------------------------------------------

  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setMaxSpeed(5000); //SPEED = Steps / second
  stepper.setAcceleration(1000); //ACCELERATION = Steps /(second)^2
  stepper.setSpeed(2000);
  //---------------------------------------------------------------------------

}

void loop() //this function constantly polls the pins. It is not the most efficient practice...
{
  while (digitalRead(limitSwitch_1) == LOW)
  {
    stepper.runSpeed(); //step the motor (this will step the motor by 1 step
  }
}

You need to go through the documentation and examples for AccelStepper and familiarize yourself with how to change direction, etc.

The bounce example illustrates the ping-pong oscillation between limits (no switches).

@jremington that example worked great!!! Thanks for directing me to it. Now to figure out how to incorporate that with the while loop and endstop sensors.

Any additional sources to reference?

// Bounce.pde
//
// Make a single stepper bounce from one limit to another
//

#include <AccelStepper.h>
// Define a stepper and the pins it will use
// direction Digital 9 (CCW), pulses Digital 8 (CLK)
AccelStepper stepper(1, 8, 9);
void setup()
{
  // Change these to suit your stepper if you want
  stepper.setMaxSpeed(5000);
  stepper.setAcceleration(1000);
  stepper.moveTo(500);
}
void loop()
{
  // If at the end of travel go to the other end
  if (stepper.distanceToGo() == 0)
    stepper.moveTo(-stepper.currentPosition());
  stepper.run();
}

Ok. I'm making somewhat progress. I was able to research and find code to use two momentary buttons to oscillate direction. Please see below.

But it's not exactly what I'm looking to do. I'd like optical endstops switches to automatically change the direction. A little help?

Thanks!

#define DISTANCE 800

int StepCounter = 0;
int Stepping = false;

void setup() {
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);

  pinMode(2, INPUT);
  pinMode(3, INPUT);
}

void loop() {
  if (digitalRead(3) == LOW && Stepping == false)
  {
    digitalWrite(8, LOW);
    Stepping = true;
  }
  if (digitalRead(2) == LOW && Stepping == false)
  {
    digitalWrite(8, HIGH);
    Stepping = true;
  }

  if (Stepping == true)
  {
    digitalWrite(9, HIGH);
    delay(1);
    digitalWrite(9, LOW);
    delay(1);

    StepCounter = StepCounter + 1;

    if (StepCounter == DISTANCE)
    {
      StepCounter = 0;
      Stepping = false;
    }
  }
}

I also found this forum post that would do exactly what i want!
https://forum.arduino.cc/index.php?topic=619075.0

But I am uncertain what 4 pins are being used as constants as shown below. I know what to do with Pins 2 and 3, though. Could someone please help me out?

Thanks!

// constants won't change. They're used here to set pin numbers:[color=#222222][/color]
const int ForwardPin = 12;     // the number of the pin that when high sends slide forward[color=#222222][/color]
const int StopForwardPin = 8;     // the number of the pin that stops motor when forward[color=#222222][/color]
const int BackwardPin = 11;     // the number of the pin that when high sends slide backward[color=#222222][/color]
const int StopBackwardPin = 9;  // the number of the pin that stops the motor when backward[color=#222222][/color]
int smDirectionPin = 2; //Direction pin[color=#222222][/color]
int smStepPin = 3; //Stepper pin

I'd like optical endstops switches to automatically change the direction.

I don't understand why this is so difficult for you. Here is how the bounce example switches direction:

  // If at the end of travel go to the other end
  if (stepper.distanceToGo() == 0)
    stepper.moveTo(-stepper.currentPosition());

All you need to do is read the optical endstop switch and change the if clause to test for closure.

If you don't understand how the examples work, you won't get anywhere with them. So, make sure that you understand exactly what each line does, and why, before moving on.

@jremington This is difficult for me because I am just starting out. I really do appreciate your input.

What do you mean "test for closure"? Could you show me by example? I'm not asking you to write the code, just that's how I learn.

Thanks so much.

If you don't know how to test a switch for closure, start by studying the Arduino IDE example code, and on line tutorials.

In in IDE, go to the menu and study
file>Examples>01.Basic>DigitalReadSerial
file>Examples>02.Digital>Button
file>Examples>02.Digital>DigitalInputPullup
file>Examples>02.Digital>StateChangeDetection

Example of a tutorial with a wiring diagram: https://www.arduino.cc/en/Tutorial/BuiltInExamples/Button

Thank you so much! I’ll research your suggestions.

Did you happen to check the the old post that I mentioned? I questioned the pin assignments because they didn’t match the schematic that was provided. Your thoughts?

Thanks again!

I DID IT!!!!!!!!!!!!!!!!!!! :smiley: :smiley: :smiley:

I got the oscillation to work! The key was to compare the switches since I couldn't get it to stop reversing once I unblocked a switch.

@jremington I really appreciate your guidance! I know it's frustrating to deal with new people, so thank you

#include <AccelStepper.h> //accelstepper library

// constants won't change. They're used here to set pin numbers:
const byte limitSwitch_1 = 2; //pin for the microswitch
const byte limitSwitch_2 = 3; //pin for the microswitch
int smDirectionPin = 9; //Direction pin
int smStepPin = 8; //Stepper pin

// variables will change:
int switch1State = 0;         // variable for reading the Switch 1 status
int switch2State = 0;         // variable for reading the Switch 2 status
boolean ForwardState = 0;         // variable for reading the micro-switch status
boolean BackwardState = 0;   // variable for reading the microswitch status
//------------------------------------------------------------------------------------

//direction Digital 9 (CCW), pulses Digital 8 (CLK)
AccelStepper stepper(1, 9, 8);

void setup()
{
  //Limit Switches
  pinMode(limitSwitch_1, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(limitSwitch_2, INPUT_PULLUP); // internal pullup resistor (debouncing)
  //---------------------------------------------------------------------------

  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setMaxSpeed(5000); //SPEED = Steps / second
  stepper.setAcceleration(1000); //ACCELERATION = Steps /(second)^2

  pinMode(smDirectionPin, OUTPUT);
  pinMode(smStepPin, OUTPUT);
}

void SlideForward()
{
  stepper.setSpeed(2000);
}

void SlideBackward()
{
  stepper.setSpeed(-2000);
}

void loop()
{
  // read the state of the switches value
  ForwardState = digitalRead(limitSwitch_1);
  BackwardState = digitalRead(limitSwitch_2);

  //step the motor (this will step the motor by 1 step at each loop indefinitely)
  stepper.runSpeed();

  // check which pin 2 or 3 is HIGH:
  if (ForwardState == HIGH && BackwardState == LOW) {
    SlideForward();
  }

  if (BackwardState == HIGH && ForwardState == LOW) {
    SlideBackward();
  }
}

so this is getting really fun! Exciting to see things working.

I'm trying to further enhance my code by adding a start/stop button to the loop. Since, my final if clause checks to see of either of the switches is ON, the sketch will only start if I trip an optical endstop.

I found an exButton library and modified the code per their instructions. The Start/Stop functionality still works, but I still have to initially trip a sensor to get it going.

Ideally, I'd like the motor to start traveling either forward or backward toward a stop to start my mechanism.

A little help?

Thanks!

#include <AccelStepper.h> //accelstepper library

// constants won't change. They're used here to set pin numbers:
const byte limitSwitch_1 = 2; //pin for the microswitch
const byte limitSwitch_2 = 3; //pin for the microswitch
int smDirectionPin = 9; //Direction pin
int smStepPin = 8; //Stepper pin

// variables will change:
int switch1State = 0;         // variable for reading the Switch 1 status
int switch2State = 0;         // variable for reading the Switch 2 status
boolean ForwardState = 0;         // variable for reading the micro-switch status
boolean BackwardState = 0;   // variable for reading the microswitch status
//------------------------------------------------------------------------------------

//direction Digital 9 (CCW), pulses Digital 8 (CLK)
AccelStepper stepper(1, 9, 8);

void setup()
{
  //Limit Switches
  pinMode(limitSwitch_1, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(limitSwitch_2, INPUT_PULLUP); // internal pullup resistor (debouncing)
  //---------------------------------------------------------------------------

  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setMaxSpeed(5000); //SPEED = Steps / second
  stepper.setAcceleration(1000); //ACCELERATION = Steps /(second)^2

  pinMode(smDirectionPin, OUTPUT);
  pinMode(smStepPin, OUTPUT);

}

void SlideForward()
{
  stepper.setSpeed(2000);
}

void SlideBackward()
{
  stepper.setSpeed(-2000);
}

void loop()
{
  // read the state of the switches value
  ForwardState = digitalRead(limitSwitch_1);
  BackwardState = digitalRead(limitSwitch_2);

  //step the motor (this will step the motor by 1 step at each loop indefinitely)
  stepper.runSpeed();

  // check which pin 2 or 3 is HIGH:
  if (ForwardState == HIGH && BackwardState == LOW) {
    SlideForward();
  }

  if (BackwardState == HIGH && ForwardState == LOW) {
    SlideBackward();
  }
}

this is the code with ezButton library.

#include <AccelStepper.h> //accelstepper library
#include <ezButton.h>

#define LOOP_STATE_STOPPED 0
#define LOOP_STATE_STARTED 1

// constants won't change. They're used here to set pin numbers:
const byte limitSwitch_1 = 2; //pin for the microswitch
const byte limitSwitch_2 = 3; //pin for the microswitch
int smDirectionPin = 9; //Direction pin
int smStepPin = 8; //Stepper pin

// variables will change:
int switch1State = 0;         // variable for reading the Switch 1 status
int switch2State = 0;         // variable for reading the Switch 2 status
boolean ForwardState = 0;         // variable for reading the micro-switch status
boolean BackwardState = 0;   // variable for reading the microswitch status
//------------------------------------------------------------------------------------

// create ezButton object that attach to pin 7;
ezButton button(7);
int loopState = LOOP_STATE_STOPPED;

//direction Digital 9 (CCW), pulses Digital 8 (CLK)
AccelStepper stepper(1, 9, 8);

void setup()
{
  //Limit Switches
  pinMode(limitSwitch_1, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(limitSwitch_2, INPUT_PULLUP); // internal pullup resistor (debouncing)
  //---------------------------------------------------------------------------

  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setMaxSpeed(5000); //SPEED = Steps / second
  stepper.setAcceleration(1000); //ACCELERATION = Steps /(second)^2

  pinMode(smDirectionPin, OUTPUT);
  pinMode(smStepPin, OUTPUT);

  // set debounce time to 50 milliseconds
  button.setDebounceTime(50);
}

void SlideForward()
{
  stepper.setSpeed(2000);
}

void SlideBackward()
{
  stepper.setSpeed(-2000);
}

void loop() {
  button.loop(); // MUST call the loop() function first

  if (button.isPressed()) {
    if (loopState == LOOP_STATE_STOPPED)
      loopState = LOOP_STATE_STARTED;
    else // if(loopState == LOOP_STATE_STARTED)
      loopState = LOOP_STATE_STOPPED;
  }


  if (loopState == LOOP_STATE_STARTED) {
    // read the state of the switches value;
    ForwardState = digitalRead(limitSwitch_1);
    BackwardState = digitalRead(limitSwitch_2);

    //step the motor (this will step the motor by 1 step at each loop indefinitely)
    stepper.runSpeed();

    // check which pin 2 or 3 is HIGH:
    if (ForwardState == HIGH && BackwardState == LOW) {
      SlideForward();
    }

    if (BackwardState == HIGH && ForwardState == LOW) {
      SlideBackward();
    }
  }
}