Hello, I'm looking for some help controlling two Nema 14 Bipolar stepper motors using two Pololu A4988 stepper drivers. I aim to move the stepper motors at the same time back and forth, in opposite directions.
The current code I have runs each motor on a button press, but they are running one after the other. When I try to put the code for the two stepper motors together, the motors jerk little distances at a time. Is this due to blocking? Do I need to be using millis()? If so, some coding suggestions would be appreciated.
Im using digitalWrite(enPin1, HIGH); to disable power to the stepper motor so that it won't use power/overheat when sitting idle for long periods.
#include <AccelStepper.h>
#define dirPinLeft 7
//#define stepPinLeft 8
#define stepPin 8 // shared step pin so both motors move at the same time
#define dirPinRight 3
//#define stepPinRight 4
#define motorInterfaceType 1
const int enPin1 = 9; // enable pin 1
const int enPin2 = 10; // enable pin 2
const int buttonPin = 5; // button pin
int buttonState = 0; // variable for reading the pushbutton status
AccelStepper stepperLeft = AccelStepper(motorInterfaceType, stepPin, dirPinLeft);
AccelStepper stepperRight = AccelStepper(motorInterfaceType, stepPin, dirPinRight);
void setup() {
stepperLeft.setMaxSpeed(200);
stepperLeft.setAcceleration(150);
stepperRight.setMaxSpeed(200);
stepperRight.setAcceleration(150);
pinMode(buttonPin, INPUT);
pinMode(enPin1, INPUT);
pinMode(enPin2, INPUT);
digitalWrite(buttonPin, HIGH); // activate internal pull up
digitalWrite(enPin1, HIGH); // disable motor control
digitalWrite(enPin2, HIGH); // disable motor control
}
void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
// check if the pushbutton is pressed. If it is, the buttonState is HIGH:
if (buttonState == LOW) {
leftMove();
rightMove();
} else {
digitalWrite(enPin1, HIGH); // disable motor control
digitalWrite(enPin2, HIGH); // disable motor control
}
}
void leftMove() {
digitalWrite(enPin1, LOW); // enable motor control
for (int c = 0; c < 1; c++) {
stepperLeft.moveTo(-35);
while (stepperLeft.currentPosition() != -35) // Full speed up to -35
stepperLeft.run();
stepperLeft.stop(); // Stop as fast as possible: sets new target
stepperLeft.runToPosition();
// Now stopped after quickstop
stepperLeft.moveTo(35); // go backwards
while (stepperLeft.currentPosition() != 0) /// Full speed back to 0
stepperLeft.run();
stepperLeft.stop(); /// Stop as fast as possible: sets new target
stepperLeft.runToPosition(); /// stopped
}
digitalWrite(enPin1, HIGH); /// disable motor control
}
void rightMove() {
for (int c = 0; c < 1; c++) {
digitalWrite(enPin2, LOW); /// enable motor control
stepperRight.moveTo(35);
while (stepperRight.currentPosition() != 35) /// Full speed up tp -35
stepperRight.run();
stepperRight.stop(); /// Stop as fast as possible: sets new target
stepperRight.runToPosition();
// Now stopped after quickstop
stepperRight.moveTo(-35); // go backwards
while (stepperRight.currentPosition() != -35) // Full speed back to 0
stepperRight.run();
stepperRight.stop(); // Stop as fast as possible: sets new target
stepperRight.runToPosition(); // stopped
}
digitalWrite(enPin2, HIGH); // disable motor control
}
// MultiStepper.pde
// -*- mode: C++ -*-
//
// Shows how to multiple simultaneous steppers
// Runs one stepper forwards and backwards, accelerating and decelerating
// at the limits. Runs other steppers at the same time
//
// Copyright (C) 2009 Mike McCauley
// $Id: MultiStepper.pde,v 1.1 2011/01/05 01:51:01 mikem Exp mikem $
#include <AccelStepper.h>
// Define some steppers and the pins the will use
AccelStepper stepper1; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
AccelStepper stepper2(AccelStepper::FULL4WIRE, 6, 7, 8, 9);
AccelStepper stepper3(AccelStepper::FULL2WIRE, 10, 11);
void setup()
{
stepper1.setMaxSpeed(200.0);
stepper1.setAcceleration(100.0);
stepper1.moveTo(24);
stepper2.setMaxSpeed(300.0);
stepper2.setAcceleration(100.0);
stepper2.moveTo(1000000);
stepper3.setMaxSpeed(300.0);
stepper3.setAcceleration(100.0);
stepper3.moveTo(1000000);
}
void loop()
{
// Change direction at the limits
if (stepper1.distanceToGo() == 0)
stepper1.moveTo(-stepper1.currentPosition());
stepper1.run();
stepper2.run();
stepper3.run();
}// MultiStepper.pde
// -*- mode: C++ -*-
//
// Shows how to multiple simultaneous steppers
// Runs one stepper forwards and backwards, accelerating and decelerating
// at the limits. Runs other steppers at the same time
//
// Copyright (C) 2009 Mike McCauley
// $Id: MultiStepper.pde,v 1.1 2011/01/05 01:51:01 mikem Exp mikem $
#include <AccelStepper.h>
// Define some steppers and the pins the will use
AccelStepper stepper1; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
AccelStepper stepper2(AccelStepper::FULL4WIRE, 6, 7, 8, 9);
AccelStepper stepper3(AccelStepper::FULL2WIRE, 10, 11);
void setup()
{
stepper1.setMaxSpeed(200.0);
stepper1.setAcceleration(100.0);
stepper1.moveTo(24);
stepper2.setMaxSpeed(300.0);
stepper2.setAcceleration(100.0);
stepper2.moveTo(1000000);
stepper3.setMaxSpeed(300.0);
stepper3.setAcceleration(100.0);
stepper3.moveTo(1000000);
}
void loop()
{
// Change direction at the limits
if (stepper1.distanceToGo() == 0)
stepper1.moveTo(-stepper1.currentPosition());
stepper1.run();
stepper2.run();
stepper3.run();
}
Since you need a button-press, consider adapting the built-in state change detection example to control the targets of the steppers:
Switches are more often wired to ground and an input set to INPUT_PULLUP so that an external resistor is not required. My state change for active low inputs tutorial shows switch wiring and example code.
Thanks everyone. I got it working by following the MultiStepper example but also by enabling and disabling the steppers at certain points. I will work on incorporating the buttons as per advice.
This is the working code:
#include <AccelStepper.h>
#define dirPinLeft 7
#define stepPinLeft 8
#define dirPinRight 3
#define stepPinRight 4
#define motorInterfaceType 1
AccelStepper stepperLeft = AccelStepper(motorInterfaceType, stepPinLeft, dirPinLeft);
AccelStepper stepperRight = AccelStepper(motorInterfaceType, stepPinRight, dirPinRight);
const int enPin1 = 9; // enable pin 1
const int enPin2 = 10; // enable pin 2
//const int buttonPin = 5; // button pin
//int buttonState = 0; // variable for reading the pushbutton status
void setup() {
stepperLeft.setMaxSpeed(1000);
stepperLeft.setAcceleration(300);
stepperLeft.moveTo(30);
stepperRight.setMaxSpeed(1000);
stepperRight.setAcceleration(300);
stepperRight.moveTo(-30);
//pinMode(buttonPin, INPUT);
pinMode(enPin1, INPUT);
pinMode(enPin2, INPUT);
//digitalWrite(buttonPin, HIGH); // activate internal pull up
digitalWrite(enPin1, HIGH); // disable motor control
digitalWrite(enPin2, HIGH); // disable motor control
}
void loop() {
slowMove();
}
void slowMove() {
digitalWrite(enPin1, LOW); // enable motor control
digitalWrite(enPin2, LOW); // enable motor control
if (stepperLeft.distanceToGo() == 0)
stepperLeft.moveTo(-stepperLeft.currentPosition());
stepperLeft.run();
if (stepperRight.distanceToGo() == 0)
stepperRight.moveTo(-stepperRight.currentPosition());
stepperRight.run();
digitalWrite(enPin1, HIGH); // disable motor control
digitalWrite(enPin2, HIGH); // disable motor control
}
Does anyone know how I limit the number of times the motor moves back and forth? I suspect distanceToGo() might not be appropriate, or maybe run() is not right. Can't quite get my head around it.
How many times? If it is, for example, 5 times after every button press, I'd use the state-detection example, set a static or global variable to 5, and then decrement it each time a cycle completes.
Consider how this new 'processEnablerStateVariableCount' variable works in this modified State Change Detection example:
/*
State change detection (edge detection)
Often, you don't need to know the state of a digital input all the time,
but you just need to know when the input changes from one state to another.
For example, you want to know when a button goes from OFF to ON. This is called
state change detection, or edge detection.
This example shows how to detect when a button or button changes from off to on
and on to off.
The circuit:
* pushbutton attached to pin 2 from +5V
* 10K resistor attached to pin 2 from ground
* LED attached from pin 13 to ground (or use the built-in LED on
most Arduino boards)
created 27 Sep 2005
modified 30 Aug 2011
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/ButtonStateChange
*/
// this constant won't change:
const int buttonPin = 2; // the pin that the pushbutton is attached to
const int ledPin = 13; // the pin that the LED is attached to
// Variables will change:
int buttonPushCounter = 0; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button
int processEnablerStateVariableCount = 0; // state variable for 5-count process enabling
void setup() {
// initialize the button pin as a input:
pinMode(buttonPin, INPUT);
// initialize the LED as an output:
pinMode(ledPin, OUTPUT);
// initialize serial communication:
Serial.begin(9600);
}
void loop() {
// read the pushbutton input pin:
buttonState = digitalRead(buttonPin);
// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == HIGH) {
// if the current state is HIGH then the button
// wend from off to on:
buttonPushCounter++;
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter);
if(processEnablerStateVariableCount == 0 ){
processEnablerStateVariableCount = 5 ; // initialize count
Serial.println("Meta-cycle count initiated");
}
} else {
// if the current state is LOW then the button
// wend from on to off:
Serial.println("off");
if (buttonPushCounter % 4 == 0){
if(processEnablerStateVariableCount > 0) {
--processEnablerStateVariableCount;
Serial.print("Meta-cycles left: ");
Serial.println(processEnablerStateVariableCount);
}
}
}
// Delay a little bit to avoid bouncing
delay(50);
}
// save the current state as the last state,
//for next time through the loop
lastButtonState = buttonState;
// turns on the LED every four button pushes by
// checking the modulo of the button push counter.
// the modulo function gives you the remainder of
// the division of two numbers:
if (buttonPushCounter % 4 == 0) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
By setting an maintaining a "state variable", you can then use it in a conditional like if(processEnablerStateVariableCount > 0){...} as a virtual button or switch for controlling other things.
In your use-case, you could set a similar state variable with a button press, and decrement it with each oscillation, and make movement dependent on if(processEnablerStateVariableCount > 0)
Any amount of times really. I'd like to use this nonblocking function (a la MultipleSteppers) but a for loop doesn't work (it works perfectly without the for loop, but it moves indefinitely):
void test01() {
for (int c = 0; c < 2; c++) {
digitalWrite(enPin1, LOW); /// enable motor control
digitalWrite(enPin2, LOW); /// enable motor control
if (stepperLeft.distanceToGo() == 0)
stepperLeft.moveTo(-stepperLeft.currentPosition());
stepperLeft.run();
if (stepperRight.distanceToGo() == 0)
stepperRight.moveTo(-stepperRight.currentPosition());
stepperRight.run();
digitalWrite(enPin1, HIGH); /// disable motor control
digitalWrite(enPin2, HIGH); /// disable motor control
}
}
Instead, I thought I'd try a simple function like this, but it jams up and doesn't move, with or without the for loop:
void test01() {
stepperLeft.setMaxSpeed(2000);
stepperLeft.setAcceleration(300);
stepperRight.setMaxSpeed(2000);
stepperRight.setAcceleration(300);
stepperLeft.moveTo(35); /// Set the target position:
stepperRight.moveTo(-35); /// Set the target position:
// for (int c = 0; c < 2; c++) {
digitalWrite(enPin1, LOW); /// enable motor control
digitalWrite(enPin2, LOW); /// enable motor control
stepperLeft.run(); /// Run to target position with set speed and acceleration/deceleration:
stepperRight.run(); /// Run to target position with set speed and acceleration/deceleration:
stepperLeft.moveTo(0); /// Move back to zero:
stepperRight.moveTo(0); /// Move back to zero:
stepperLeft.run();
stepperRight.run();
digitalWrite(enPin1, HIGH); /// disable motor control
digitalWrite(enPin2, HIGH); /// disable motor control
// }
}
Does anyone know if there's another method in the AccelStepper library to do this? Thanks.
int processEnablerStateVariableCount = 2; // global state variable for remembering how many times to go
void test01() {
for (int c = 0; c < 2; c++) {
digitalWrite(enPin1, LOW); /// enable motor control
digitalWrite(enPin2, LOW); /// enable motor control
if (stepperLeft.distanceToGo() == 0 && processEnablerStateVariableCount > 0){
stepperLeft.moveTo(-stepperLeft.currentPosition());
--processEnablerStateVariableCount; // reduce the count by one
}
stepperLeft.run();
if (stepperRight.distanceToGo() == 0 && processEnablerStateVariableCount > 0)
stepperRight.moveTo(-stepperRight.currentPosition());
stepperRight.run();
digitalWrite(enPin1, HIGH); /// disable motor control
digitalWrite(enPin2, HIGH); /// disable motor control
}
}
Thank you so much, @DaveX. I didn't fully grok your first suggestion of using processEnablerStateVariableCount, but seeing it in situ has dropped the penny. I now have it working as advertised, so thanks again for your help.
#include <AccelStepper.h>
#define dirPinLeft 7 /// direction left
#define stepPinLeft 8 /// step left
#define dirPinRight 3 /// direction right
#define stepPinRight 4 /// step right
#define motorInterfaceType 1 /// bipolar motor
AccelStepper stepperLeft = AccelStepper(motorInterfaceType, stepPinLeft, dirPinLeft);
AccelStepper stepperRight = AccelStepper(motorInterfaceType, stepPinRight, dirPinRight);
const int enPin1 = 9; /// enable pin 1
const int enPin2 = 10; /// enable pin 2
const int buttonPin = 5; /// button pin
int buttonState = 0; /// variable for reading the pushbutton status
int processEnablerStateVariableCount = 5; // global state variable for remembering how many times to go
void setup() {
pinMode(buttonPin, INPUT_PULLUP); // Button between Pin and Ground
pinMode(enPin1, INPUT);
pinMode(enPin2, INPUT);
digitalWrite(buttonPin, HIGH); /// activate arduino internal pull up
digitalWrite(enPin1, HIGH); /// disable motor control
digitalWrite(enPin2, HIGH); /// disable motor control
stepperLeft.setMaxSpeed(100); /// slow is 100, fast is 900000
stepperLeft.setAcceleration(50);
stepperLeft.moveTo(15);
stepperRight.setMaxSpeed(100);
stepperRight.setAcceleration(50);
stepperRight.moveTo(-15);
}
void loop() {
slowMove1();
}
void slowMove1() { /// motors are NOT disabled at end of function
digitalWrite(enPin1, LOW); /// enable motor control
digitalWrite(enPin2, LOW); /// enable motor control
if (stepperLeft.distanceToGo() == 0 && processEnablerStateVariableCount > 0) {
stepperLeft.moveTo(-stepperLeft.currentPosition());
--processEnablerStateVariableCount; /// reduce the count by one
}
stepperLeft.run();
if (stepperRight.distanceToGo() == 0 && processEnablerStateVariableCount > 0) {
stepperRight.moveTo(-stepperRight.currentPosition());
}
stepperRight.run();
digitalWrite(enPin1, HIGH); /// disable motor control
digitalWrite(enPin2, HIGH); /// disable motor control
}
*Unfortunately for my application, this code leaves the stepper motors enabled (i.e., locked in place and therefore getting hot). It's odd because the function below disables them. Thoughts?
void slowMove2() { /// motors are disabled at end of function
for (int c = 0; c < 2; c++) {
digitalWrite(enPin1, LOW); /// enable motor control
digitalWrite(enPin2, LOW); /// enable motor control
if (stepperLeft.distanceToGo() == 0)
stepperLeft.moveTo(-stepperLeft.currentPosition());
stepperLeft.run();
if (stepperRight.distanceToGo() == 0)
stepperRight.moveTo(-stepperRight.currentPosition());
stepperRight.run();
digitalWrite(enPin1, HIGH); /// disable motor control
digitalWrite(enPin2, HIGH); /// disable motor control
}
}
My first thought would be to not try to make the functions clean up after themselves, but to add a watchdog to periodically disable them when not in use, using something like this untested code:
but you then need to enable them when you need to move them:
void slowMove2() { for (int c = 0; c < 2; c++) {
if( ! motorsEnabled ){
digitalWrite(enPin1, LOW); /// enable motor control
digitalWrite(enPin2, LOW); /// enable motor control
motorsEnabled = true;
}
if (stepperLeft.distanceToGo() == 0)
stepperLeft.moveTo(-stepperLeft.currentPosition());
stepperLeft.run();
if (stepperRight.distanceToGo() == 0)
stepperRight.moveTo(-stepperRight.currentPosition());
stepperRight.run();
}
}
but my second thought would be that since slowMove1() is the only thing going on in your loop and you aren't doing other moves, it is fine to clean up after itself. It is enabling and disabling the steppers at the beginning and end of each call, and this routing is most of each loop, so they are only disabled for the time it takes for loop() to loop. You could move the processEnablerStateVariableCount
condition around the body of the function:
void slowMove1() {
if (processEnablerStateVariableCount > 0){
...
}
}
In this case, it only enables the steppers when needed.
steppermotors are allowed to warm up to 50 to 70 °C without any problem
But you have to avoid getting hotter. If the become hotter than 70°C the magnets are damaged and loose strength.
Without the coils beeing powered permanently you are in danger to loose steps and then your code does no longer know what the exact position of your steppermotor is.
You haven't described anything about your project. So no advice possible.
Except you switch off the coil-current and rotate the stepper-motors by hand they act as electricity generators. Depending on the rpm the amount of voltage created in this way may damage your stepper-motor-driver.