Stepper, L293d, limit switches and stepping function

Hello

I’m hitting a wall with a project. I’m coding control for a motorised camera slider. I have two limit switches that turn off power to the stepper when closed. I have tried code that tells the stepper to move a particular number of steps, which the program executes regardless of the state of the limit switches.

So I know what I need to do, more or less, but am not sure how exactly to go about it. In order for the limits to be effective, I need a stepping function that moves one full step, and in my loop I need to call this function as many times as I need to move the slider carraige around.

I’m using the L293d because I have now successfully bricked three A4988 boards for different but similar reasons and have no more.

I know the problem is in my singleStep() function. I’m using a bipolar stepper (4 wires). For now I’ve got a delay() between on / off states in the step function, which I intend to replace with delaymicrosecond() once I get it working at all.

For now, with this code, the motor jitters back and forth and then stalls.

Do I need to do something like this? http://www.tigoe.net/pcomp/code/circuits/motors/stepper-motors/

Step wire 1wire 2wire 3wire 4
1 High low high low
2 low high high low
3 low high low high
4 high low low high

#include <Stepper.h>

int in1Pin = 16;
int in2Pin = 17;
int in3Pin = 14;
int in4Pin = 13;

Stepper motor(512, in1Pin, in2Pin, in3Pin, in4Pin);
int en1Pin = 3;
int en2Pin = 4;

int limit1 = 18; //slider limit switch left, motor end
int limit2 = 19; // slider limit switch right, idler pulley end


#define goLEFT  0
#define goRIGHT 1


void setup()
{
  pinMode(limit1, INPUT_PULLUP);
  pinMode(limit2, INPUT_PULLUP);

  pinMode(in1Pin, OUTPUT);
  pinMode(in2Pin, OUTPUT);
  pinMode(in3Pin, OUTPUT);
  pinMode(in4Pin, OUTPUT);

  pinMode(en1Pin, OUTPUT);
  pinMode(en2Pin, OUTPUT);

  // this line is for Leonardo's, it delays the serial interface
  // until the terminal window is opened
  while (!Serial);

  Serial.begin(9600);
  motor.setSpeed(10);
}

boolean leftLimit() {
  return digitalRead(limit1);
}
boolean rightLimit() {
  return digitalRead(limit2);
}


void eStop() {
  if ( leftLimit() == 0 || rightLimit() == 0) {
    disengage();
  }
}

void singleStep() {

  digitalWrite(in1Pin, HIGH);
  digitalWrite(in2Pin, LOW);
  digitalWrite(in3Pin, HIGH);
  digitalWrite(in4Pin, LOW);

  delay(7);
  digitalWrite(in1Pin, LOW);
  digitalWrite(in2Pin, HIGH);
  digitalWrite(in3Pin, LOW);
  digitalWrite(in4Pin, HIGH);
  delay(7);

}

void loop() {
  engage();
  eStop();
  singleStep();
}

void engage() {
  digitalWrite(en1Pin, HIGH);
  digitalWrite(en2Pin, HIGH);
}

void disengage() {
  digitalWrite(en1Pin, LOW);
  digitalWrite(en2Pin, LOW);
}

Why are you trying to write your own singleStep() function when you could use the Stepper library ?

I am confused about how you plan to use the switches. You say " I have two limit switches that turn off power to the stepper when closed." but then you contradict this by saying “move a particular number of steps, which the program executes regardless of the state of the limit switches”

If the switches turn off the power no amount of code could make the motors move.

Turning off the power in the wrong way is a good way to ruin an A4988 (or equivalent).

If you want the code to read the switch and just allow the motor to move while the switch is not pressed it is very simple. Something like this pseudo code …

if (numSteps < maxSteps) {
   if (limitSwitch is not pressed) {
     singleStep();
     numSteps += 1;
   }
}

Post a link to the datasheet for your stepper motor.

…R
Stepper Motor Basics

Hi Robin,

with the code I posted, while the motors don't run the way I want them to, the limit switch code at least does work. The code I used before this was from a tutorial here Arduino Code | Arduino Lesson 16. Stepper Motors | Adafruit Learning System
...where the stepper moves the amount of steps input over the serial monitor. So while that amount of steps was being turned, the state of the switches was being ignored.

This is the datasheet:Document Viewer | Open ImpulseOpen Impulse

reading your link Robin, and ordered more A4988’s…

It looks like those motors need 1.7amps which may be a bit of a struggle for A4988s. They will probably need heat sinks and fans. Set the current limit to 1 amp to start with - that should be enough to get the motors to move reliably without load.

The motors I have only require 0.33 amps so I have no experience of A4988s with higher currents.

The Pololu DRV8825 can handle a little more current - but it looks like it is too late for that option.

...R

ok so I got a couple of DRV8825 boards. I’ve got it wired up to work in 1/32 microstepping mode and it works ok with my limit switches.

However I’m sort of hitting a wall here with how I’m going about my code. I’m using a 3D mouse via Ableton Live to send MIDI to the Teensy, which I want to be able to control which direction the motor moves in, and also to change the delay between steps, and thus the speed. Maybe my logic is faulty with that; either way, the motor makes some whiney noises when goLEFT of goRIGHT is selected but doesn’t move.

Here’s my beautiful code…

#include <Servo.h>
Servo focus;
int pos = 0; // servo position variable

#include <Wire.h>

#define stepPin 17
#define dirPin 16
#define sliderEnable 12

int manualTilt = A9; //pin 23
int manualPan = A7; // pin 21
int manualSlider = A8; //pin 22
int manualFocus = A0; //pin 14
int absPanVal = 0;

int numSteps = 2000;
int limit1 = 18; //slider limit switch left, motor end
int limit2 = 19; // slider limit switch right, idler pulley end
byte lSval; // limit switch value

int maxSteps = 12000;


#define goLEFT  0
#define goRIGHT 1

void setup()
{
  Serial.begin(9600);
  // slide.setMaxSpeed(400);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(limit1, INPUT_PULLUP);
  pinMode(limit2, INPUT_PULLUP);
  pinMode(sliderEnable, OUTPUT);
  sliderHome(); //runs routine to home motor
  focus.attach(4);  // attaches the servo on pin 4
}

boolean leftLimit() {
  return digitalRead(limit1);
}
boolean rightLimit() {
  return digitalRead(limit2);
}


void eStop() {
  if ( leftLimit() == 0 || rightLimit() == 0) {
    digitalWrite(sliderEnable, HIGH);
  }
}

void singleStep(int steps) {


  Serial.println(steps);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(2);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(steps);
}

void moveSlider(boolean dir, int speedy) {
  digitalWrite(sliderEnable, LOW);

  if (dir == goLEFT) {
    digitalWrite(dirPin, LOW);
    //   Serial.println("going LEFT");
    singleStep(speedy);
    numSteps += -1;
  } else if (dir == goRIGHT) {
    digitalWrite(dirPin, HIGH);
    //   Serial.println("going RIGHT");
    singleStep(speedy);
    numSteps += 1;
  }
}

void sliderHome() {

  while (leftLimit() == 0 || rightLimit() == 0) {
    moveSlider(goLEFT, 10000);
    //singleStep();
  }
}

void midiControl() {
  if (usbMIDI.read()) {
    byte type  = usbMIDI.getType(); //3 = CC
    byte chan  = usbMIDI.getChannel();
    byte data1  = usbMIDI.getData1();
    byte data2  = usbMIDI.getData2();

    //  Serial.print(type); Serial.print(" "); Serial.print(chan); Serial.print(" ");
    if (type == 3 && chan == 1 && data1 == 6) {
      int panMidiVal = map(data2, 0, 127, -64, 63); //scale incoming MIDI CC to allow for forwards/backwards

      if (panMidiVal >= 4) {
        absPanVal = abs(panMidiVal); //absolute value for PWM
        int absRightVal = map(absPanVal, 4, 64, 200, 1000); // mapped to 0-255
        //  Serial.println(absRightVal);
        moveSlider(goRIGHT, absRightVal);

      } else if (panMidiVal < -4) {

        int absLeftVal = map(panMidiVal, -64, -2, 2000, 4000); // mapped to 0-255
        //        if (absRightVal > maxSteps) {
        moveSlider(goLEFT, absLeftVal);
        // singleStep();
        //}
      }
      else if (panMidiVal <= 3 && panMidiVal >= -3) {

        digitalWrite(sliderEnable, HIGH);
      }

    }
  }
}

void MIDIfocusControl() { //FOCUS movement
  if (usbMIDI.read()) {
    byte type  = usbMIDI.getType(); //3 = CC
    byte chan  = usbMIDI.getChannel();
    byte data1  = usbMIDI.getData1();
    byte data2  = usbMIDI.getData2();
    if (type == 3 && chan == 1 && data1 == 8) {
      int focusPos = data2 * 2;
      //      Serial.println("focusPos = ");
      //      Serial.println(focusPos);
      focus.write(focusPos); //map 0-128 to 0-255 by multiplying by 2
    }
  }
}

void loop() {
  //  sliderHome();
  eStop();
  // moveSlider(goLEFT, 200);
  midiControl();
  // MIDIfocusControl();
  // manualFocus();
}

What is this line supposed to do ?

moveSlider(goLEFT, absLeftVal);

I wonder if your code is confusing the position the motor should go to and the speed it should go at?

You probably (almost certainly) should change your singleStep() function so that it does not use delay() because the Arduino can do nothing during a delay(). Have a look at the second example in this simple stepper code.

...R

Thanks for your prompt reply Robin! it’s entirely probable I’m mixing up steps and speed, takes a couple of days for my brain to warm up to Arduino so should be able to understand better in the morning. In the example you mentioned, am I right in thinking that the millisBetweenSteps variable is what dictates the speed?

While this project is taking a lot longer to do than expected, I am learning slowly but surely…

deved:
am I right in thinking that the millisBetweenSteps variable is what dictates the speed?

Yes

...R

I've tried the code you linked but am running into a problem with the millis. At 25 milliseconds between steps I get no movement. I think this is because I have the DRV8825 wired to work in 1/32 microstep mode, so I'm guessing the motor is being told to move one microstep every 25 milliseconds. I changed the value to 1 and it does move, very slowly.

Can micros() be substituted, or will that lead to other issues? Or can millis have a float value, e.g. 0.1millis?

Of course you can use micros().

One of the really nice things about the Arduino system is that you could have figured that out with a test program in less time than it took you to write the question.

Also, start with full steps until you understand how the thing works. Then you can change things gradually and understand the effects of your changes.

...R

I got the example code you posted above working only when I uncommented the pulsewidth delay…
delayMicroseconds(pulseWidthMicros); // probably not needed

The confusing thing is now that my code works if I call the moveSlider(int dir) function directly from the loop, but when I use the 3D mouse to change direction, the motor just makes a series of high pitched noises. I don’t see why though…

#include <Servo.h>
Servo focus;
int pos = 0; // servo position variable

#include <Wire.h>

#define stepPin 17
#define dirPin 16
#define sliderEnable 12

int limit1 = 18; //slider limit switch left, motor end
int limit2 = 19; // slider limit switch right, idler pulley end

unsigned long curMillis;
unsigned long prevStepMillis = 0;
int pulseWidthMicros = 20;  // microseconds
unsigned long millisBetweenSteps = 1; // milliseconds

#define goLEFT  0
#define goRIGHT 1

void setup()
{
  Serial.begin(9600);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(sliderEnable, OUTPUT);
  pinMode(limit1, INPUT_PULLUP);
  pinMode(limit2, INPUT_PULLUP);

    sliderHome(); //runs routine to home motor
  focus.attach(4);  // attaches the servo on pin 4
}

boolean leftLimit() {
  return digitalRead(limit1);
}
boolean rightLimit() {
  return digitalRead(limit2);
}


void eStop() {
  if ( leftLimit() == 0 || rightLimit() == 0) {
    digitalWrite(sliderEnable, HIGH);
  }
}

void singleStep() {
  if (curMillis - prevStepMillis >= millisBetweenSteps) {
    prevStepMillis += millisBetweenSteps;
   // Serial.println(millisBetweenSteps);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(pulseWidthMicros);
    digitalWrite(stepPin, LOW);
  }
}

void moveSlider(boolean dir) {

  if (dir == goLEFT) {
    digitalWrite(dirPin, LOW);
    digitalWrite(sliderEnable, LOW);
    singleStep();

  }
  if (dir == goRIGHT) {
    digitalWrite(dirPin, HIGH);
    digitalWrite(sliderEnable, LOW);
    singleStep();
  }
}

void midiControl() {
  if (usbMIDI.read()) {
    byte type  = usbMIDI.getType(); //3 = CC
    byte chan  = usbMIDI.getChannel();
    byte data1  = usbMIDI.getData1();
    byte data2  = usbMIDI.getData2();

    if (type == 3 && chan == 1 && data1 == 6) {
      int panMidiVal = map(data2, 0, 127, -64, 63); //scale incoming MIDI CC to allow for forwards/backwards

      if(panMidiVal >= 4) {
        moveSlider(goRIGHT);

      }else if(panMidiVal < -4) {

        moveSlider(goLEFT);
      }
      else if (panMidiVal <= 3 && panMidiVal >= -3) {

        digitalWrite(sliderEnable, HIGH);
      }

    }
  }
}


void loop() {
  curMillis = millis();
  eStop();
//moveSlider(goRIGHT);
  midiControl();
}

deved:
The confusing thing is now that my code works if I call the moveSlider(int dir) function directly from the loop, but when I use the 3D mouse to change direction, the motor just makes a series of high pitched noises. I don't see why though...

Before I look at your code please tell me what tests you have carried out to try to find the cause of the problem.

...R