Rotating NEMA 23 CNC Motor After Laser Breaks

Hello,

I’m working on a project where I need a NEMA 23 CNC motor to rotate 60 deg every time a laser breaks. I’m using an Arduino Uno R3 and a TB6600 4.5A CNC single-axis stepper motor drive. As of now I can get it to work but for some reason the motor will only run twice before nothing responds afterwards (laser breaks, motor turns, laser breaks, motor turns … nothing responds). I’m wondering if it’s my code, or if my arduino simply cannot process a bigger motor/driver, or if I just need a better motor driver. The motor driver is powered by a 12V 10A power supply (also tried with 24V 10A power supply to see if it was a power issue but same results). The code is displayed below. Also, I need the motor to rotate precisely (i.e. start at 0, move exactly to 60 deg, then to 120, then to 180 …etc until it reaches back to 0/360). Right now, the motor steps a couple times once power is turned on, and when it rotates it is not consistent in moving to desired spot. I need some help to be able to fine tune this. The links to the motor and motor driver are also below. Thanks in advance!

Stepper Motor Driver: http://www.dx.com/p/tb6600-4-5a-cnc-single-axis-stepper-motor-driver-board-controller-418451#.WXkAMvnyvIU

Stepper Motor: https://www.omc-stepperonline.com/hybrid-stepper-motor/nema-23-bipolar-18deg-126nm-1784ozin-28a-25v-57x57x56mm-4-wires-23hs22-2804s.html

*The LED in the code is simply to see if the signal from laser is working.

#include <AccelStepper.h>

AccelStepper stepper (1, 8, 9);

#define LED 2

#define SENSORPIN 3

// variables will change:
int sensorState = 0, lastState = 0;       // variable for reading the pushbutton status

void setup()
{
  // initialize the LED pin as an output:
  pinMode(LED, OUTPUT);

  // initialize the sensor pin as an input:
  pinMode(SENSORPIN, INPUT_PULLUP);
  digitalWrite(SENSORPIN, HIGH); // turn on the pullup (May need to change.)

  Serial.begin(9600);

  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(500);

}

void loop()
{
  // read the state of the pushbutton value:
  sensorState = digitalRead(SENSORPIN);

  // check if the sensor beam is broken
  // if it is, the sensorState is LOW:

  if (sensorState != lastState)
  {
    if (sensorState == LOW)
    {
      // turn LED on:
      digitalWrite(LED, HIGH);

      delay(1000);

      digitalWrite(LED, LOW);

      delay(500);

      stepper.move(33.33);
      while (stepper.currentPosition() != 19)
        stepper.run();

      stepper.stop();
      stepper.runToPosition();
    }
  }
  delay(50);

  if (sensorState && !lastState)
  {
    Serial.println("Unbroken");
  }
  if (!sensorState && lastState)
  {
    Serial.println("Broken");
  }
  lastState = sensorState;
}
stepper.move(33.33);

stepper.move takes a long integer, either 33 or 34, 59.4 or 61.2 degrees, you might get closer with microstepping (with multiples of 4, 8, or 16) but I don't believe the motor has much holding torque, if any, at partial steps, also I believe you need stepper.run at the head of loop(), it needs to run as often as possible.

One more thing, have you set the current limiter on your drive correctly? If set too high the drive may be overheating and cutting out.

This is all very confusing and needs a complete re-think

    stepper.move(33.33);
      while (stepper.currentPosition() != 19)
        stepper.run();

      stepper.stop();
      stepper.runToPosition();

This while (stepper.currentPosition() != 19) calls stepper.run() in all except one situation. In what circumstances will that arise?

Then it hands over to runToPosition() which will be painfully slow because of the huge delay()s in your code. Also, I have no idea what position it is supposed to run to.

Normally you would have stepper.run() in loop() rather than in a WHILE and you would not have any delay()s in loop()

Both run() and runToPosition() use acceleration so they should not really be interrupted before the destination is reached. You can use runSpeed() and runSpeedToPosition() if you don’t need acceleration.

I don’t think this is a programming problem. I believe it is a thinking problem - you need to figure out your control logic. I find that writing it down carefully helps me think through issues and make sure I don’t miss bits.

…R

You shouldn't mix relative moves and absolute position testing!

move() is relative, currentPosition() is absolute.

You should always call run() in loop all the time. I think you should lose all the currentPosition loop,
use only absolute calls (moveTo()), and then your code will be much clearer.

@edgemoron … I figured it worked that way. I will change it to 33, but I’m assuming a full rotation is 200. I tested it with 200 to see if it would fully rotate and it undershot a full rotation a bit and when it would run the 2nd time, it would undershoot the undershot once moved zip tie on shaft back to initial position. It’s losing steps somehow and that’s why I think it might be the motor driver. The motor has a rated current at 2.8A so I adjusted the current as close to 2.8A as possible.

@Robin2 … I used the example code for a quick stop. I’m attempting to rotate the motor 60 deg only when the laser has been tripped once and for it to keep doing that until I cut the power. It needs to be precise. I will try your runSpeed() and runSpeedToPosition(). The main issue I’m having is trying to understand the library. I’ve been throughout the provided examples and files that show where the code is coming from but I don’t understand how the commands work exactly.

@MarkT … I will clean the code up a bit and let everyone know how it goes.

So I cleaned it up to look like what's shown below. It performs how I would like it to however it's not precise. The value 207 appeared to be a full 360. As I kept tripping the sensor, it would sometimes return the full 360 and then other times undershoot it. How do I get it to rotate the exact amount and nothing more or less? Thanks in advance.

void loop()
{
  // read the state of the pushbutton value:
  sensorState = digitalRead(SENSORPIN);

  // check if the sensor beam is broken
  // if it is, the sensorState is LOW:

  if (sensorState != lastState)
  {
    if (sensorState == LOW)
    {
      // turn LED on:
      digitalWrite(LED, HIGH);

      delay(1000);

      digitalWrite(LED, LOW);

      delay(500);

      stepper.move(207);

      stepper.runToPosition();
    }
  }
  delay(50);

  if (sensorState && !lastState)
  {
    Serial.println("Unbroken");
  }
  if (!sensorState && lastState)
  {
    Serial.println("Broken");
  }
  lastState = sensorState;
}

If you are using the beam break to "home" the motor, you should only need to do it once. The way I would do it is:
Step the motor toward the beam at a moderate speed, stop at the break, then step slowly the other way just until the beam makes again and call that "home" position, shouldn't need to do it again unless the motor is jammed and misses steps. As far as the 60 degrees goes, you might try alternating between 33 steps twice then 34 once or 33,34,33,33,34,33,33.....

jmcmahan:
So I cleaned it up to look like what's shown below.

Remove ALL the delay()s and move stepper.runToPosition(); out of the IF and put it as the very last thing in loop().

For your LED blinks have a look at how millis() is used to manage timing without blocking in Several things at a time

The way the AccelStepper library works is that you must call the run...() function many times for each step (as many times as possible) and the library will figure out when a step is due based on the speed that you have set. It cannot function properly if you use delay() or any other piece of code that prevents loop() from repeating very quickly.

...R

edgemoron:
If you are using the beam break to "home" the motor, you should only need to do it once. The way I would do it is:
Step the motor toward the beam at a moderate speed, stop at the break, then step slowly the other way just until the beam makes again and call that "home" position, shouldn't need to do it again unless the motor is jammed and misses steps. As far as the 60 degrees goes, you might try alternating between 33 steps twice then 34 once or 33,34,33,33,34,33,33.....

Wish I looked at this reply earlier. Ended up doing exactly that! Thank you very much for your help!