4 stepper motors control: max3 running simlutaneously, start stop independently

patrickgoh1993:
I need something that doesn't require loop function so that I can create functions for each motor.

No you don't. You just need to organize your program properly. You can have

motorA.run();
motorB.run();
motorC.run();

in loop()

...R

Robin2:
No you don't. You just need to organize your program properly. You can have

motorA.run();

motorB.run();
motorC.run();



in loop()

...R

Hi I will give it a go. Another question is how to write button bit. I have push to break button and a switch case that read will the potentiometer. I'm struggling to write up the button part so that when it's pressed it will run certain function based on the analogRead from potentiometer.

int butPin = 22;
pinMode (butPin, INPUT);

//check if button is pressed
 if (digitalRead(butPin) == 0){
   switch(i){
     case 1: function1();
     break;
     case 2:  function2();
     break;
     case 3: function3();
     break;
     case 4: function4();
     break;
     case 5: function5();
     break;
     case 6: function6();
     break;
     case 7: function7();
     break;
     case 8: function8();
     break;
     case 9: function9();
     break;
     case 10: function10();
     break;
     default: ;
   }

Pretty sure it's wrong and I still need debouncing. Any guidance is much appreciated.

patrickgoh1993:
and a switch case that read will the potentiometer.

Shouldn't that be "Peter the potentiometer" ?

I recommend that you read the switch state into a variable and use that in the IF test. It means you can print the value to see if it is what you think it should be.

Pretty sure it's wrong

What happens when you try it?

If it does not work please post the whole program.

...R

Whole program (1/2):

#include <Wire.h> //
#include <LiquidCrystal_I2C.h>//
#include <avr/pgmspace.h>
#include <AccelStepper.h>
#include <MultiStepper.h>

//Back view//A Top//B Down//C Left//D Right 
AccelStepper stepperA(AccelStepper::DRIVER, 12, 11);
AccelStepper stepperB(AccelStepper::DRIVER, 10, 9);
AccelStepper stepperC(AccelStepper::DRIVER, 7, 6);
AccelStepper stepperD(AccelStepper::DRIVER, 4, 3);

// Up to 10 steppers can be handled as a group by MultiStepper
MultiStepper steppers;

const char string_0[] PROGMEM = "Welcome!";
const char string_1[] PROGMEM = "Sex On The Beach";
const char string_2[] PROGMEM = "Screwdriver";
const char string_3[] PROGMEM = "Rum Sunset";
const char string_4[] PROGMEM = "Orange Crush";
const char string_5[] PROGMEM = "Cosmo";
const char string_6[] PROGMEM = "Vodka C'berry";
const char string_7[] PROGMEM = "Vodka Metro";
const char string_8[] PROGMEM = "Gimlet";
const char string_9[] PROGMEM = "Gin Sunset";
const char string_10[] PROGMEM = "Caipirinha";

const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5, string_6, string_7, string_8, string_9, string_10};
char buffer[50];    // make sure this is large enough for the largest string it must hold
int analogPin = 0;
int result = 0;
int oldResult = 0;
int i = 0;

const int buttonPin = 2;     // the number of the pushbutton pin 
int buttonState;      // the current reading from the input pin
int lastButtonState = LOW;
// the following variables are unsigned long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers


LiquidCrystal_I2C lcd(0x3F,16,2);//

void setup()
{
  Serial.begin(9600);
  while (!Serial);
  Serial.println("OK");
  lcd.init();//

  // Turn on the backlight
  lcd.backlight();//
  delay(100);

//Configure button
pinMode(buttonPin, INPUT);

// Configure each stepper
stepperA.setMaxSpeed(29); //steps per sec
stepperB.setMaxSpeed(1400); 
stepperC.setMaxSpeed(400); //set this to match eul ratio of sms to position
stepperD.setMaxSpeed(400); //This ensure that under the same 1 second, motor C and D will complete 3 turns while motor A turns (say) 20 steps in the same 1 second.
 
// Then give them to MultiStepper to manage
  steppers.addStepper(stepperA);
  steppers.addStepper(stepperB);
  steppers.addStepper(stepperC);
  steppers.addStepper(stepperD);
}

////////////////////////DRINKS///////////////////////DRINKS/////////////////////////////////////DRINKS////////////////////////////////////
void function1(){ //Sex On The Beach
   Serial.println("Case 1");
motorA(50);
  }

void function2(){ //Screwdriver
   Serial.println("Case 2");
motorCD(2);
  }

void function3(){ //Rum Sunset
 Serial.println("Case 3");
 motorB(1);
  }

void function4(){ //Orange Crush
   Serial.println("Case 4");
   motorCD(4);
   motorA(3);
  
  }

void function5(){  //Cosmo
motorC(5);
motorCD(6);
  }

void function6(){ // Vodka C'berry
motorCD(6);
delay(500);
motorACD(3);
  }

void function7(){  //Vodka Metro
motorCD(7);
  }

void function8(){  //Gimlet
motorCD(8);
  }

void function9(){  //Gin Sunset
motorCD(9);
  }

void function10(){  //Caipirinha
motorCD(10);
  }

Whole program (2/2):

///////////////////FUNCTIONS//////////////////////////////////////////////FUNCTIONS//////////////////////////////FUNCTIONS////////////////////////////
///Open MultiStepper3 to verify each function///
///////////////////////////////////////////////
//Motor function A//x from -3 to +3 but not 0//to call Carousel to move only
void motorA(int a) {
  long positions[4];            // Array of desired stepper positions. 4 for 4 steppers
  positions[0] = 29*a;            //number on steps based on stp per rev (don't forget microstepping settings)
  positions[1] = 0;
  positions[2] = 0;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

//Motor function B//y either +1 or -1 only//to call Pusher up OR down only
void motorB(int b) {
  long positions[4];            
  positions[0] = 0;             //number on steps based on spr
  positions[1] = b*800;
  positions[2] = 0;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

//Motor function C//
void motorC(int c) {
  long positions[4];            
  positions[0] = 0;             //number on steps based on spr
  positions[1] = 0;
  positions[2] = c*200;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

//Motor function D//
void motorD(int d) {
  long positions[4];            
  positions[0] = 0;             //number on steps based on spr
  positions[1] = 0;
  positions[2] = 0;
  positions[3] = d*200;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }
  
//Motor function AC//to call Carousel to move and C starts pumping//Every alcohol gap(29steps) C/D pumps 2 revs
void motorAC(int e) {
  long positions[4];           //Array of desired stepper positions
  positions[0] = 29*e;         //number on steps based on spr
  positions[1] = 0;
  positions[2] = 200*e;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

void motorAD(int f) {
  long positions[4];           //Array of desired stepper positions
  positions[0] = 29*f;         //number on steps based on spr
  positions[1] = 0;
  positions[2] = 0;
  positions[3] = 400*f;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

//Motor functionBC//fixed//Every up one way OR down (29steps), C/D pumps 9 rev.
void motorBC() {

 long positions[4];            // Array of desired stepper positions
  positions[0] = 0;            //number on steps based on spr
  positions[1] = 8000;
  positions[2] = 2000;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }
  
//Motor function-BC//fixed
void motornBC() {
 long positions[4];            // Array of desired stepper positions
  positions[0] = 0;            //number on steps based on spr
  positions[1] = -8000;
  positions[2] = 2000;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  } 

//Motor functionBD//fixed
void motorBD() {
 long positions[4];            // Array of desired stepper positions
  positions[0] = 0;            //number on steps based on spr
  positions[1] = 8000;
  positions[2] = 2000;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }
  
//Motor function-BD//fixed
void motornBD() {
 long positions[4];            // Array of desired stepper positions
  positions[0] = 0;            //number on steps based on spr
  positions[1] = -8000;
  positions[2] = 2000;
  positions[3] = 0;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  } 
  
//Motor function ACD//Every alcohol gap(29steps) C/D pumps 2 revs.
void motorACD(int g) {
  long positions[4];
  positions[0] = 29*g;
  positions[1] = 0;
  positions[2] =400*g;
  positions[3] = 400*g;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

//Motor function BCD//fixed//Every up one way OR down (29steps), C/D pumps 9 revs.
void motorBCD() {
  long positions[4];
  positions[0] = 8000;
  positions[1] = 0;
  positions[2] =2000;
  positions[3] = 2000;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

//Motor function nBCD (fixed)
void motornBCD() {
  long positions[4];
  positions[0] = -8000;
  positions[1] = 0;
  positions[2] =2000;
  positions[3] = 2000;
  steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
  }

//Motor function CD ()
void motorCD(int h) {
  long positions[4];
  positions[0] = 0;
  positions[1] = 0;
  positions[2] = 200*h;
  positions[3] = 200*h;
 steppers.moveTo(positions);
  steppers.runSpeedToPosition(); // Blocks until all are in position
}

///////////////////////////////   RUNNING CODE   /////////////////////////////////////////////
void loop(){
  result = analogRead(analogPin);
  i = result / 100; //more drinks --> try result / 50
  //Serial.println(i); //To check what potentiometer reads: uncomment this
  if (i != oldResult){
    lcd.clear();//
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
    lcd.home();//
    lcd.print(buffer);//
    delay(200);
  }
  oldResult = i;
///////////////////////////////////   BUTTON   //////////////////////////////////////////////
// read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;
      
      // only toggle the LED if the new button state is HIGH
     if (buttonState == LOW) {
      Serial.println(i);
        switch(i){
          case 0:;
          break;
          //case 1: function1();
          Serial.println("Case 1");
          case 1: function1();
          break;
          //case 2:  function2();
          Serial.println("Case 2");
          case 2: function2();
          break;
         // case 3: function3();
          Serial.println("Case 3");
          case 3: function3();
          break;
          //case 4: function4();
          Serial.println("Case 4");
          case 4: function4();
          break;
          //case 5: function5();
          Serial.println("Case 5");
          case 5: function5();
          break;
         // case 6: function6();
          Serial.println("Case 6");
          case 6: function6();
          break;
          //case 7: function7();
          Serial.println("Case 7");
          case 7: function7();
          break;
          //case 8: function8();
          Serial.println("Case 8");
          case 8: function8();
          break;
          //case 9: function9();
          Serial.println("Case 9");
          case 9: function9();
          break;
          //case 10: function10();
          Serial.println("Case 10");
          case 10: function10();
          break;
          default: ;
 } 
}
}
  }
  lastButtonState = reading;
  }

Sorry, but for a long program just add the .ino file as an attachment. When joining two fragments it is very easy to introduce an error.

...R

Program attached. To summarise my stepper motors (A,B,C,D)problem:
i. A start(turning)-A stop-B start(goes up)-B stop(very short interval)-B start(goes down)-A start(turning)- A stop-B start...

ii. C and/or D start-(for a period of time)-C and/or D stop.

I want to have i and ii running together but independent of each other.

In my program(it didn't work but that's my logic), I call C and D in each function in order for C and D to continue running. Sort of making C and D running continuously. Assumed the image attached is a drink recipe, motor ACD works well when jump to next line everything went wrong: all motors start to move.

I have now gave up(time constraint) and running A-B-C-D in order. :frowning:

Final_Code_Complex_Debounce.ino (10.6 KB)

You are using runSpeedToPosition() which blocks everything until the move is complete. Remove all those lines.
Then, as the last thing in loop() add the line steppers.run();

You will also need to remove all delay()s from loop() and anything else that might reduce the repeat rate.

You don't actually need the MultiStepper library. You could just as easily put

stepperA.run();
stepperB.run();
etc

as the last thing in loop()

...R

Thank you. I will try it out tomorrow. Another question on the Accelstepper library. Here is a rough sketch of what I want to find out using a push to break button:

void setup() {
  Serial.begin(9600);
  stepperC.setMaxSpeed(800);
  stepperC.setAcceleration(1000);
}

void motorC() {
  stepperC.runToNewPosition(400); 
  delay(50);
}

void function1(){
  motorC();
  delay(50);
  motorC();

void loop(){
  if (digitalRead(pin) ==LOW){
    function1();
  }
}

If I pressed once, will it move 800 steps or it will only move 400 because the second time I call function motorC() it's already at 400?

I read the library reference it says something about moving in relative OR treating steps as coordinates.

What will happen if I pressed second time?

If I want it to run it in a way that when I call motorC() once, it moves 400 steps, call it again, it moves ANOTHER 400 steps(which means it's 800 steps from power-on position), what should I write in the program?

patrickgoh1993:
If I pressed once, will it move 800 steps or it will only move 400 because the second time I call function motorC() it's already at 400?

I believe it will only move once - i.e. to position 400. But you could have figured that out with a short test in almost the same time it took you to write your question. The Arduino system is great for learning-by-doing.

More importantly the whole concept in your little illustration code in Reply #14 is all wrong for the sort of interactive system you are trying to build. You should not have any delay()s in your code and you should not have any blocking stepper moves in your code. When you press a button it should update the value in the variable that defines the stepper position. It should NOT directly make the stepper move. The code to make the stepper move should just check the value of the position and if it is not at that position it should make it move.

Think of it almost like two completely separate programs. There is a user-input part that updates some variables and nothing else. That part of the code has nothing to do with motors and does not need to know that motors exist. Then there is a motor-control part that only knows about the values in the variable and has no idea how those values got there.

Imagine you are out in your garden and you tell your son or daughter to write "please feed the dog" on a scrap of paper and push it through the letter box on your front door. Your other son picks up the paper from the doormat and reads the message and gets the dog's meal ready.

Separating the code like this also makes development and testing much easier. The two parts can be tested independently of each other.

...R

Hi Robin, the illustration code in #13 was to run motors in sequence in the order or A-B-C-D now. Unfortunately I don't have the motors with me all the time that's why I have posted that question.

I have uploaded the full code that I will be using for the project.

Functions to control each motor, B is fixed.

stepperA.setMaxSpeed(29.0); //
  stepperA.setAcceleration(1.0);  //
  stepperB.setMaxSpeed(1400.0); //1600
  stepperB.setAcceleration(1200.0);  //1200
  stepperC.setMaxSpeed(1400.0); //1400
  stepperC.setAcceleration(1000.0);//1000
  stepperD.setMaxSpeed(800.0); //max for pump long
  stepperD.setAcceleration(2400.0);  //try go higher
}

// (4) motor A function
void motorA(int A) { //set 0 at bottle. steps= coordinates
  //pos feed bottle 6, neg feed bottle 1)
  stepperA.runToNewPosition(A);  //(STEPBGO)see below
}

// (5) motor B function
void motorB() {
  stepperB.runToNewPosition(5600);  //(STEPBGO)see below //-5600
  delay(3500); //time to empty optics. //3500
  stepperB.runToNewPosition(0); //position based on the (STEPBGO) taken. Zero means back to starting position.
}

// (5.0) motor BD function (double shots)-call this first before (5)
void motorBD() {
  stepperB.runToNewPosition(-400);  //(STEPBGO)see below  //-5600
  delay(1500); //time to empty optics. //3500
  stepperB.runToNewPosition(50); //position based on the (STEPBGO) taken. Zero means back to starting position.
}

// (6) motor C function
void motorC() {
  stepperC.move(4000); //  200 step per rev
  stepperC.runToPosition();
}

//(7) motor D function
void motorD() {
  stepperD.move(4000);
  stepperD.runToPosition();

And functions for drinks:

void function1() { //Sex On The Beach
  Serial.println(buffer); //Serial print is to check if the function is running
  motorA(30);
  motorB();
  motorC();
  motorD();
  motorA(0);
  Serial.println("Done");
}

void function2() { //Screwdriver
  Serial.println(buffer);
  motorA(-30);
  motorB();
  motorC();
  motorA(0);
  Serial.println("Done");
}

I have tested every function A,B,C, and D and it works well. When I call function1() using switch case in loop function(see full code), it seems to stuck after first line of function1().

What has gone wrong?

AlternativeRunning.ino (7.35 KB)

In desperate need to have a working code today :frowning:

patrickgoh1993:
In desperate need to have a working code today :frowning:

I've just woken up, so don't blame me :slight_smile:

This code is strange

        case 0:;
            break;
            //case 1: function1();
            Serial.println("Case 1");
          case 1: function1();
            break;
            //case 2:  function2();
            Serial.println("Case 2");

I don't know what happens to code that is after break and before the next case statement. Remove it as a precaution.

I have tested every function A,B,C, and D and it works well. When I call function1() using switch case in loop function(see full code), it seems to stuck after first line of function1().

How do you know? You don't seem to have any Serial.print() statements to let you follow the progress of the code. Maybe temporarily add a print at the end of each function such as Serial.println("motorA finished");

...R

Hi function1() works.
I think things went wrong in the loop function because in order for user to select drinks I have put the drink functions in the loop function. Could you please elaborate more on my loop function? I can twist and push, and checked with serial print which shows it actually go into the selected drink function.

Post your most recent code that shows function1 (horrible choice of name by the way) working. What is the new problem?

HI I have two codes. One is for testing the other one is for real run. Under function test, the motor functions executed well and motor moves. Under the full function(real run), the motor functions are executed accordingly, but motors didn't move.

AlternativeRunning.ino (7.62 KB)

functiontestforAlternativeRunning.ino (1.94 KB)

Which "Case n" do you see on the serial monitor? What happens if you call the corresponding function from your test version of the program?

I called function 1 in the testing code at setup(), and it works. Case n appears as their corresponding function. i.e pressed drink 1, case 1 appeared.

And do you see the serial output of Buffer and all those "Done n" prints?

I'm assuming you do but that the motors aren't moving.

Yes, I do see all the prints but just not moving.