How to control stepper motors simultaneously with acceleration

BrenCar12:
No it still has to be in the for loop. Will using millis() help or no.

You will need to explain in detail how you want things to work. I can't understand why there would need to be a long delay() within the scope of the movement of the motor.

Perhaps you can describe the process as a series of steps like this (my details are probably all wrong, but should illustrate what I mean.

  • System starts
  • Stepper move to Home Position
  • User enters data for next project
  • User presses GO
  • Motor moves to first position
  • Camera is triggered
  • Motor moves to second position
  • etc
  • etc

Incidentally a list like that can be the framework for the program structure.

...R

Yes, I can give you a list like that.

  • System starts
  • Steppers move to home - I haven't written this in my code yet but it is no problem as I know of a code that works that I just removed for testing the motor movement in timelapse
  • User enters the no. of photos wanted taken, the interval between each photo, the in and out position of where they want the slider to start and stop
  • Once all that data is completed the user presses go and it calculates the steps per photo as the motor needs to move so many steps then stop to take the photo then move again and the stopping is necessary to stop camera shake.
  • The camera shutter is triggered
  • Before the motor moves there needs to be a delay to allow the photo to be finished taking as sometimes the photo I will be taking will have the camera shutter open for 30 seconds where no movement can happen otherwise the photo will be blurry
  • After this delay the motor can move the steps per photo (to the next position) while in the interval 'delay' between each photo and for example if it takes the motor 1 second to move and the interval is 3 seconds long, the motor will need to move after the previous photo is taken and then there will need to be a delay of 2 seconds to make the full 3-second interval between each photo
  • After the motor finishes moving and the delay is completed it will need to check the pause switch to make sure the user hasn't paused the timelapse (the code) to do something
  • If the pause switch is activated the code pauses until it is deactivated by the user
  • If the pause switch isn't activated or is deactivated, the process from the camera shutter triggering onwards repeats until the final photo is finished being taken
  • Once this is complete the user is brought back to where they can enter the data for the next timelapse

There is also a second part where there can be a timelapse of just the entire length of the slider but I have no issue with this part of the code. Also so you know the part that is all good is the void playTimeLapse() part. It is the void playInOutTimeLapse() part that I have the issue with as I need to do what we have been discussing throughout this whole forum thread.

I hope this is helpful for you to help me figure this out. Also, I would like to thank you for all the help you have given me so far.

Cheers,
Brennen

Thanks, that's a big help.

Lets focus (pun intended) on these few stages (I hope I have interpreted your description correctly). I am starting somewhere in your 4th bullet point which would probably have been better written as a number of separate bullets.

  • User presses GO
  • logic will repeat from here
  • identify next position
  • identify time interval for next photo
  • save start move time
  • motor moves freely to position (without any delay()s involved)
  • check if time is correct for next photo
  • save start photo time
  • take photo
  • check if time is correct for next move
  • repeat

I would use millis() for all of the time keeping - it just makes things less confusing. Something like this pseudo code

get number of steps to next position
get nextPhotoInterval
get exposureDuration
startMoveTime = millis()
cameraReady = false
move motor number of steps // assuming this is blocking code with a FOR loop
if cameraReady == false and (millis() - startMoveTime >= nextPhotoInterval) {
   cameraReady = true
   startPhotoTime = millis()
   code to operate the camera
   photoBeingTaken = true
}
if (photoBeingTaken == true and millis() - startPhotoTime >= exposureDuration) {
   photoBeingTaken = false
}
// repeat as needed

...R

The code sort makes sense but do I incorporate the motor moving code where it says "move motor number of steps" and how do I repeat that all until the last photo is taken, do I just put it in a for loop?

Brennen

BrenCar12:
The code sort makes sense but do I incorporate the motor moving code where it says "move motor number of steps"

Yes, but you will need to think about the details

and how do I repeat that all until the last photo is taken, do I just put it in a for loop?

It's best to forget about FOR and WHILE. Allow loop() to do the repetition and have a variable with the number of photos to be taken. Every time a photo is take decrement the variable. When it gets to 0 stop.

Organise the program as a series of short single purpose functions with meaningful names. Your code in loop() might be as roughly like this

void loop() {
    if (sequenceNotStarted == true) {
        getUserInput(); // this will change sequenceNotStarted to false
                        // and get all the necessary values, including numPhotos
                        // set waitingForGo = true
    }
    if (sequenceNotStarted == false and waitingForGo == true) {
        checkGoButton();
    }
    if (waitingForGo == false) {
        if (numPhotos > 0) {
            takeNextPhoto();
        }
    }
}

The code to move the motor etc would go into the takeNextPhoto() function.

Have a look at Planning and Implementing a Program

...R

Ok thanks so much for that. I will write the new code today and check it tonight and fingers crossed it will work but by the looks of what u have written it will be good. Also in the void loop I need to put the code for the screen and joystick movement to loop over as well right?

BrenCar12:
Also in the void loop I need to put the code for the screen and joystick movement to loop over as well right?

Put the screen code in its own function and the joystick code in its function and call those functions from loop().

Don't update the screen more often than the minimum needed for the human eye to perceive a smooth system - 5 or 10 times per second is probably ample.

...R

Robin2:
Don't update the screen more often than the minimum needed for the human eye to perceive a smooth system - 5 or 10 times per second is probably ample.

How do I only update that only 5 times a second in the loop?

Robin2:
Put the screen code in its own function and the joystick code in its function and call those functions from loop().

Also, the screen code is already in its own function so that is sorted.

BrenCar12:
How do I only update that only 5 times a second in the loop?

if (millis() - lastScreenUpdateTime >=  screenUpdateInterval) {
   lastScreenUpdateTime = millis();
   updateScreen();
}

Get in the habit of thinking in millis :slight_smile:

...R

I was just testing the code I wrote from the pseudo-code you gave me and so I just put the takeNextPhoto() in the loop so it would constantly play over and over. I then connected my multimeter to the ground and shutter pin to make sure it was being triggered. When I uploaded it to my Arduino to test it, it moved the motor and then did nothing else for at least a minute and then it just constantly powered the shutter pin. Here is the test code I have.

byte xStepPIN = 2; //Slider
byte xDirPIN = 5; //Slider

int shutterPIN = 11;

int startMoveTime;
int cameraReady;
int startPhotoTime;
int photoBeingTaken;
int motorBeingMoved;

unsigned long curMicros;
unsigned long prevStepMicros = 0;
unsigned long slowMicrosBetweenSteps = 6000; // microseconds
unsigned long fastMicrosBetweenSteps = 1500;
unsigned long stepIntervalMicros;
unsigned long stepAdjustmentMicros;
int numAccelSteps = 200; // 100 is a half turn of a 200 step mmotor
int stepsToGo = 1000;

void setup()
{
  Serial.begin(115200);

  pinMode(shutterPIN, OUTPUT);     //to trigger the camera
  digitalWrite(shutterPIN, LOW);

  pinMode(xStepPIN, OUTPUT);
  pinMode(xDirPIN, OUTPUT);
  
  //For multiple steppers with acceration
  stepAdjustmentMicros = (slowMicrosBetweenSteps - fastMicrosBetweenSteps) / numAccelSteps;
  stepIntervalMicros = slowMicrosBetweenSteps;
}

void shutter()
{
  digitalWrite(shutterPIN, HIGH);
  delay(200);
  digitalWrite(shutterPIN, LOW);
}

void singleStep() 
{
  digitalWrite(xStepPIN, HIGH);
  digitalWrite(xStepPIN, LOW);
}

//For multiple steppers with acceleration
void moveMotor() 
{
    if (stepsToGo > 0) 
    {
        if (micros() - prevStepMicros >= stepIntervalMicros) 
        {
            prevStepMicros += stepIntervalMicros;
            singleStep();
            stepsToGo --;
            if (stepsToGo <= numAccelSteps) {
                if (stepIntervalMicros < slowMicrosBetweenSteps) {
                    stepIntervalMicros += stepAdjustmentMicros;
                }
            }
            else 
            {
                if (stepIntervalMicros > fastMicrosBetweenSteps) {
                    stepIntervalMicros -= stepAdjustmentMicros;
                }
            }
        }
    }
}

void takeNextPhoto()
{
  startMoveTime = millis();
  cameraReady = false;
  moveMotor();
  if ((cameraReady == false) && ((millis() - startMoveTime) >= 4000)) 
  {
    cameraReady = true;
    startPhotoTime = millis();
    shutter();
    
    photoBeingTaken = true;
  }
  if ((photoBeingTaken == true) and ((millis() - startPhotoTime) >= 2000))
  {
    photoBeingTaken = false;
  }
// repeat as needed
}

void loop() 
{
  takeNextPhoto();
}

Brennen

I don't have much time now - I will try to look at this again tomorrow.

In the meantime you have the opportunity for lots of valuable thinking and testing.

You might consider whether I put cameraReady = false in the correct place.

...R

I have been trying my best to work a solution for the past few days and all it seems like is that I am either going backwards or staying in the same place. I can't understand how or why the first but of code you show me won't work properly. Do you have any idea how I can get this to work as I am truely stumped.

Brennen

Apologies, I had intended to look at this last Sunday and forgot.

Have a look at the following code. I think it should work. Note that I have broken the task into more elements and how as each element ends it sets the stage for the next one.

void loop() {
   if (readyForNextPhoto == true) { // this only happens once for each photo to start the process
       readyForNextPhoto = false;
       startMoveTime= millis();
       stepsToGo = 1000; // or whatever value is appropriate
       startPhotoTime = 0;
       movingForNextPhoto = true;
   }
   else if (movingForNextPhoto == true) { // this will be called lots of times
       moveMotor();
   }
   else (if cameraReady == true) { // this will be called lots of times
       if (startPhotoTime == 0) {
           startPhotoTime = millis(); // but this will only be updated once per photo
       }
       takePhoto();
   }
   else (if photoTaken == true) { // this will be called lots of times
       waitAfterPhotoTaken();
   }
}

//========

void moveMotor() 
{
   if (stepsToGo == 0) {
       cameraReady = true;
       movingForNextPhoto = false;
       return;
   }
   if (stepsToGo > 0)  {
       if (micros() - prevStepMicros >= stepIntervalMicros) {
           prevStepMicros += stepIntervalMicros;
           singleStep();
           stepsToGo --;
           if (stepsToGo <= numAccelSteps) {
               if (stepIntervalMicros < slowMicrosBetweenSteps) {
                   stepIntervalMicros += stepAdjustmentMicros;
               }
           }
           else  {
               if (stepIntervalMicros > fastMicrosBetweenSteps) {
                   stepIntervalMicros -= stepAdjustmentMicros;
               }
           }
       }
   }
}

//============

void takePhoto() {
   if (millis() - startMoveTime >= 4000) {
       shutter();
       cameraReady = false;
       photoTaken = true;
   }
   if 
}

//============

void waitAfterPhotoTaken() {

   if (millis() - startPhotoTime >= 2000))
   {
       photoTaken  = false;
       readyForNextPhoto = true;
   }
   
}

…R

Do you happen to have anything to test this code as no matter how hard I try I still can not get this code to work correctly? I wish someone could combine Accelstepper and Multistepper and take the good from both to produce a perfect library that works for everything.

Cheers,
Brennen

brencar12:
Do you happen to have anything to test this code as no matter how hard I try I still can not get this code to work correctly? I

It's been a long time. Please post the program that represents your best attempt and tell us in detail what it actually does and what you want it to do that is different.

...R

I know it has been a long time now but I have finally got the code to work perfectly with the moving then taking a photo and then pausing and then repeating everything. My next question now is how do I have this bit of code only loop a certain number of times? This is all the code I have currently, and I just need to be able to loop the takeNextPhoto2() part. Just so you are aware the (Serial.println)s are to test it easily so I didn’t have to set up the motor steps every time and also so I didn’t have to set up my camera.

Cheers
Brennen

byte directionPin = 5;
byte stepPin = 2;

unsigned long curMicros;
unsigned long prevStepMicros = 0;
unsigned long slowMicrosBetweenSteps = 6000; // microseconds
unsigned long fastMicrosBetweenSteps = 1500;
unsigned long stepIntervalMicros;
unsigned long stepAdjustmentMicros;
int numAccelSteps = 100; // 100 is a half turn of a 200 step mmotor
int numSteps = 1000;
int stepsToGo = 500;
byte direction = 1;

unsigned long startMoveTime;
unsigned long startPhotoTime;
unsigned long readyForNextPhoto;
unsigned long cameraReady;

int photos = 10;
int shutterPIN = 11;
int seconds = 5;


int period = 3000;
unsigned long time_now = 0;

void setup() {
    Serial.begin(115200);
    Serial.println("Starting Stepper Demo with acceleration");

    pinMode(directionPin, OUTPUT);
    pinMode(stepPin, OUTPUT);
    pinMode(shutterPIN, OUTPUT);
    pinMode(10, OUTPUT);
    
    stepAdjustmentMicros = (slowMicrosBetweenSteps - fastMicrosBetweenSteps) / numAccelSteps;
    stepIntervalMicros = slowMicrosBetweenSteps;
    
}

void shutter()
{
  digitalWrite(shutterPIN, HIGH);
  delay(500);
  digitalWrite(shutterPIN, LOW);
}

void singleStep() 
{
    digitalWrite(stepPin, HIGH);
    digitalWrite(stepPin, LOW);
}

void moveMotor() 
{
  
  
    if (stepsToGo > 0) 
    {
        if (micros() - prevStepMicros >= stepIntervalMicros) 
        {
            prevStepMicros += stepIntervalMicros;
            singleStep();
            stepsToGo --;
            if (stepsToGo <= numAccelSteps) {
                if (stepIntervalMicros < slowMicrosBetweenSteps) {
                    stepIntervalMicros += stepAdjustmentMicros;
                }
            }
            else 
            {
                if (stepIntervalMicros > fastMicrosBetweenSteps) {
                    stepIntervalMicros -= stepAdjustmentMicros;
                }
            }
        }
    }
  
    //else {
        //direction = ! direction;
        //digitalWrite(directionPin, direction);
            // next two lines just for the demo
        //delay(2000);
        //Serial.println("Changing direction");
        //stepsToGo = numSteps;
        //prevStepMicros = micros();
    //}
}

void takeNextPhoto2()  // PERFECT NOW - USE THIS PART!!!!!!!!!
{
  startMoveTime = millis();
  
  while(millis() <= (startMoveTime + 2000))
  {
    moveMotor();
    Serial.println("bob");
  }
  
  startPhotoTime = millis();
  Serial.println("Hi");
  shutter();
  
  while(millis() <= (startPhotoTime + 4000))
  {
    Serial.println("john");
  }
}

brencar12:
This is all the code I have currently,

That program is not complete - there is no loop() function.

My next question now is how do I have this bit of code only loop a certain number of times?

The usual way to do that is to increment a counter in each iteration and don’t do anything when the desired number is exceeded. Roughly like this

if (counter < counterMax) {
   // do stuff
   counter ++;
}

…R

That if statement worked perfectly with only repeating the code a certain number of times. My next and hopefully final question for this is how do I get the motor to move say 500 steps each time cause currently in that if statement counter the motor only moves the once and I can't seem to find a way to reset the stepsToGo back to 500 for the next loop? Any ideas?

Cheers Brennen

brencar12:
I can't seem to find a way to reset the stepsToGo back to 500 for the next loop? Any ideas?

Please post the complete latest version of your program so I can see the question in context.

Might it be as simple as

if (stepsToGo == 0) {
   stepsToGo = 500;
}

...R

Robin2:
Might it be as simple as

Turned out it isn’t as simple as that. Here is my code. Can you help me sort that out?

byte xdirectionPin = 5;
byte xstepPin = 2;

unsigned long xcurMicros;
unsigned long xprevStepMicros = 0;
unsigned long xslowMicrosBetweenSteps = 6000; // microseconds
unsigned long xfastMicrosBetweenSteps = 1500;
unsigned long xstepIntervalMicros;
unsigned long xstepAdjustmentMicros;
int xnumAccelSteps = 100; // 100 is a half turn of a 200 step mmotor
int xnumSteps = 500;

int xstepsToGo = 500;
byte direction = 1;

unsigned long startMoveTime;
unsigned long startPhotoTime;
unsigned long readyForNextPhoto;
unsigned long cameraReady;

int photos = 10;
int shutterPIN = 11;
int seconds = 5;
int counter;

void setup() {
    Serial.begin(115200);
    Serial.println("Starting Stepper Demo with acceleration");

    pinMode(xdirectionPin, OUTPUT);
    pinMode(xstepPin, OUTPUT);
    pinMode(shutterPIN, OUTPUT);
    pinMode(10, OUTPUT);
    
    xstepAdjustmentMicros = (xslowMicrosBetweenSteps - xfastMicrosBetweenSteps) / xnumAccelSteps;
    xstepIntervalMicros = xslowMicrosBetweenSteps;
}

void shutter()
{
  digitalWrite(shutterPIN, HIGH);
  delay(500);
  digitalWrite(shutterPIN, LOW);
}

void xsingleStep() 
{
    digitalWrite(xstepPin, HIGH);
    digitalWrite(xstepPin, LOW);
}

void movexMotor() 
{
  
  
    if (xstepsToGo > 0) 
    {
        if (micros() - xprevStepMicros >= xstepIntervalMicros) 
        {
            xprevStepMicros += xstepIntervalMicros;
            xsingleStep();
            xstepsToGo --;
            if (xstepsToGo <= xnumAccelSteps) {
                if (xstepIntervalMicros < xslowMicrosBetweenSteps) {
                    xstepIntervalMicros += xstepAdjustmentMicros;
                }
            }
            else 
            {
                if (xstepIntervalMicros > xfastMicrosBetweenSteps) {
                    xstepIntervalMicros -= xstepAdjustmentMicros;
                }
            }
        }
    }
}


void takeNextPhoto2()  // PERFECT NOW - USE THIS PART!!!!!!!!!
{
  startMoveTime = millis();
  
  while(millis() <= (startMoveTime + 2000))
  {
    movexMotor();
    Serial.println("bob");
  }
  
  startPhotoTime = millis();
  Serial.println("Hi");
  shutter();
  
  while(millis() <= (startPhotoTime + 4000))
  {
    Serial.println("john");
  }
}

void loop() 
{
  if (counter < photos)
  {
    takeNextPhoto2();
    counter ++;
  }

  if (xstepsToGo == 0) 
  {
    xstepsToGo = 500;
  }
}

Thanks Brennen