Endstop not working properly to stop steppers - debounce needed or code issue?

Hi guys, I'm building an IR remote-controlled camera slider with 3 stepper motors controlling individual things on the slider.

  • Stepper 1 = LATERAL Movement (side to side)
  • Stepper 2 = ROTARY Movement (spins camera around)
  • Stepper 3 = FOCUS PULL Movement (rigged to clamp on lens that changes focus)

Here is a photo of the full circuit as of right now:

The main problem:

I'm using an old TV remote control to send IR signals to an IR receiver hooked into pin 12 on an UNO. The remote keypresses work PERFECTLY and do exactly what I want them to very reliably.

Pressing LEFT moves the motors clockwise, RIGHT moves the motors CCW, and MUTE stops all the motors based on "motor states" in the code. Everytime I press a remote button, it works every time as its intended.

Now the issue is adding a HOME endstop (and later adding multiple endstops) to trigger a "state" for the motors to STOP ALL MOVEMENT and home one of the steppers (in this case the LATERAL side-to-side stepper). See full code below to see the "motor states". Here is an example of the states for the LATERAL stepper:

CW ROTATION STATE (Lateral Stepper)

LeftTurnUp = 1
RightTurnDown = 0

CCW ROTATION STATE (Lateral Stepper)

LeftTurnUp = 0
RightTurnDown = 1

STOP ALL ROTATION (Lateral Stepper)

LeftTurnUp = 0
RightTurnDown = 0

For example when the MUTE button on the remote is pressed, the STOP ALL ROTATION state is activated and the motors stop. Works great. When LEFT or RIGHT buttons are pressed after that, the states change and work as expected.

I've added in a mechanical endstop to pin 10 to ALSO change the state to the STOP ALL ROTATION.

  • The switch's C lead is connected to 5V+ from Arduino
  • The switch's NO lead is connected to GND through a pull down 47ohm resistor
  • The NC lead is not connected.
  • The signal pin is connected to PIN 10 on the UNO
  • When the switch is off, measured 0.0v through and when pressed a reliable ~4.5v to signal pin

The endstop works intermittently at best and is nowhere near reliable enough to act as an endstop. Here is the code for the HOME endstop:

IN INT:

int homeButton = 10;        // "home" endstop


IN SETUP:

pinMode(homeButton, INPUT);


IN LOOP:

if (digitalRead(homeButton) == HIGH){ 
  RightTurnDown = 0;
  LeftTurnUp = 0;  
  delay(50);
  }

At first I thought it was a DEBOUNCING issue so tried some debouncing code (some simple, some complex) from a few sources and none of them worked, still had the intermittent switch problem.

I even tried using a SN7400 logic gate chip to mechanically debounce the switch signal and still had the same problem intermittent function.

I've tried everything I can to make the switch work and coming here is a last resort. All my attempts lead me to believe this might be a programming issue and not a problem with the circuit which is why I didn't post this in the motor forum. Another switch of the same make had the same problem so I don't think its a faulty switch.

I think I might be going about this the wrong way (I'm not a very good programmer)?

Maybe the loop is too long and the button isn't getting read in the loop fast enough and the program is "hiccuping"?

Again, the IR remote signal changes states perfectly, quickly and reliably. Is there maybe a way to 'simulate' a command from the remote by pressing the switch?

Here are the project specs in a nutshell:

  • 3 Stepper Motors all the same (200 steps/r, 2 phase, 1.2a/phase, 2.5ohm)
  • 3 A4988 Stepstick drivers (set to 1/16th mode with jumpers)
  • Power Supply is a 12V 2000ma wall wart to a 100uf capacitor
  • Keyes IR Receiver breakout
  • Using ACCELSTEPPER Library to control steppers
  • In this particular program, motors are set to different speeds as proof of concept

Here is another close up photo of the breadboard:

Here is the full code of the program:

This particular program is not really based on the relative position of the motors - the idea is that when a direction is pressed on the remote, the motor keeps moving indefinitely until an endstop is triggered at either end (or remote key pressed) and the endstop switch resets the position.

#include <IRremote.h>
#include <IRremoteInt.h>
#include <AccelStepper.h>

//INT

int receiver = 12;          // IR receiver pin
IRrecv irrecv(receiver);    // create a new instance of IR receiver
decode_results results;

int LeftTurnUp = 0;         // initial state set to 0
int RightTurnDown = 0;      // initial state set to 0

int incomingByte = 0;       // for incoming serial data
int homeButton = 10;        // "home" endstop


//STEPPER MOTORS
AccelStepper lateral(1, 3, 6);      // LATERAL Stepper:     pin 3 = step, pin 6 = direction
AccelStepper rotary(1, 4, 7);       // ROTARY Stepper:      pin 4 = step, pin 7 = direction
AccelStepper focuspull(1, 5, 8);    // FOCUS PULL Stepper:  pin 5 = step, pin 8 = direction

byte hBval;


void setup() {
 
  Serial.begin(9600);
  
//STEPPER MOTORS
  lateral.setMaxSpeed(50);        // slowest speed
  lateral.setAcceleration(3000);
  
  rotary.setMaxSpeed(100);        // medium speed
  rotary.setAcceleration(3000);

  focuspull.setMaxSpeed(500);     // fastest speed
  focuspull.setAcceleration(3000);


//IR RECEIVER
  irrecv.enableIRIn(); // Start the IR Receiver.
  delay(100);

  
//ENDSTOP
  pinMode(homeButton, INPUT);
    
}




void loop() {
 
 if (irrecv.decode(&results)) {         // we have received an IR signal
 Serial.println (results.value, DEC);   // display decimal value of signal code
 irrecv.resume();                       // receive the next value
 delay(10);
 }
 
 switch(results.value){
  
  case 2003594874: // Left Button: Rotates CW 
  LeftTurnUp = 1;
  RightTurnDown = 0;
  break;
  
  case 584651451: // Right Button: Rotates CCW
  RightTurnDown = 1;
  LeftTurnUp = 0;
  break;

  case 2464142367: // Mute Button: STOPS all motors
  RightTurnDown = 0;
  LeftTurnUp = 0;
  break;  
  
  }  


//END STOPS - ONLY WORKS INTERMITTENTLY!!!

  if (digitalRead(homeButton) == HIGH){ 
  RightTurnDown = 0;
  LeftTurnUp = 0;  
  delay(50);
  }


//STEPPER MOTOR STATES

  if (LeftTurnUp == 1)  //MOTORS TURN CLOCKWISE
  {
    lateral.moveTo(1000000); //Keep moving CW until stopped. 
    rotary.moveTo(1000000); //Keep moving CW until stopped. 
    focuspull.moveTo(1000000); //Keep moving CW until stopped.     
  }

  if (RightTurnDown == 1)  //MOTORS TURN COUNTER CLOCKWISE
  {
    lateral.moveTo(-1000000); //Keep moving CCW until stopped.
    rotary.moveTo(-1000000); //Keep moving CCW until stopped.
    focuspull.moveTo(-1000000); //Keep moving CCW until stopped.       
  }

  if (RightTurnDown == 0 && LeftTurnUp == 0)  //STOP ALL MOTOR MOVEMENT
  {    
    lateral.stop();
    rotary.stop();  
    focuspull.stop(); 
  }

    
  lateral.run();
  rotary.run();  
  focuspull.run();  

}


/* 
IR REMOTE CONTROL BUTTON CODES

MUTE BUTTON   =  2464142367   (Stops movement of motors.)
LEFT BUTTON   =  2003594874   (Rotates motors CW.)
RIGHT         =  584651451    (Rotates motors CCW.)

*/

Any help or suggestions is hugely appreciated. I realize I might have to rethink how the entire program is run. Thanks all in advance.

The switch's NO lead is connected to GND through a pull down 47ohm resistor

About 10K would be more appropriate.

Of course, using the internal pullup resistor makes wiring much easier. Connect the common (C) lead to ground. Connect the NO lead to the digital pin. HIGH means not pressed; LOW means pressed. Set the pin mode to INPUT_PULLUP.

To add to what PaulS has posted, if the switch is wired to ground with pullup you can add a cap (0.1uf) across the switch for hardware debounce.

Thanks @PaulS and @groundfungus, will try both suggestions here shortly.

Switch bounce should not matter for an end-stop. You are only interested in the first switch trigger. Whether it triggers 0 or 200 times more won't matter.

Don't use delay() in your code.

It looks like you follow .stop() with .run()

if (RightTurnDown == 0 && LeftTurnUp == 0)  //STOP ALL MOTOR MOVEMENT
  {   
    lateral.stop();
    rotary.stop(); 
    focuspull.stop();
  }

   
  lateral.run();
  rotary.run(); 
  focuspull.run();

Rather than using .stop() it might be better to set the destination value so there are no more steps for .run to take.

How do you plan to return to normal working after a limit switch is triggered ?

...R

Thanks for the info Robin, very interesting.

For this particular code I'm just trying to get the endstop to work reliably.

Eventually I will add more commands for different states for different pre-planned objectives and routines for the motors based on different keypresses.

For the HOME endstop in particular (knowing which direction the stepper has to go), I was thinking of doing something like this using accelstepper:

lateral.setCurrentPosition(0);   // This should stop the motor movement anyway by setting speed to 0 right?
// delay here?
lateral.setSpeed(500);
// delay here?
lateral.moveTo(-30);

Hopefully that would stop the lateral stepper, and move it off the switch by a centimeter or two?

I'm not sure if the above will work in practice but that's where I'm kind of headed with this.

Another endstop at the other end of the travel (opposite HOME) might behave differently and have its own code. Any thoughts if that would work?

In the end I'll probably have a total of 4 end stops for the different steppers to stop movement in different situations.

To move a stepper motor to a Home position requires code that works like this

move 1 step
  are we there yet ?
     if not, repeat

In fact you must always use code like that if end-stops are to work immediately. It is no good telling the motor to do 100 steps if 63 of them are after the end-stop.

Never use delay(). Use millis() to manage timing as illustrated in several things at a time.

I would design my system so that it used the end-stops to determine the Home position and otherwise used a count of the number of steps to avoid running off the ends. That implies choosing motors and speeds so that no steps are missed.

You might then have other limit switches (perhaps just microswitches) that brutally cut off the power to everything if they are triggered - no microprocessor involved.

...R
Stepper Motor Basics
Simple Stepper Code