# Continuously adjusting motor speed while running

Hello! I am trying to adjust the speed of a motor while it's running. The Arduino receives a string from PC to set the speed, acceleration, and running for three motors. In particular, the string has the following format: <mode, motorID, value1, value2, value3>.

• "mode" can be "SPEED", "ACCEL", and "RUN"
• "motorID" is a combination of integers 1, 2, and 3 (corresponds to three motors)
• "value1", "value2", and "value3" are the parameters set for motors under a specified mode. For example, if the "mode" is "SPEED", the values are motor speeds.
What I want the motors to do is adjusting the speed while running. So I send the following commands to Arduino:
• 1st command: <SPEED,1,100,0,0> //set speed for motor 1
• 2nd command: <ACCEL,1,5000,0,0> //set accel for motor 1
• 3rd command: <RUN,1,10000,0,0> //move 10000 steps from the current position for motor 1
• 4th command: <SPEED,1,1000,0,0> //change the speed for motor 1

After sending the first three commands, the motor can work as expected. If we assume the time that Arduino received the 3rd command as t=0s, and we send the 4th command at t=10s, I want motor 1 can increase its speed from 100 to 1000 to finish the remaining steps (roughly 10000-100x10=9000 steps). However, it seems that my code can't set the speed 1000 for motor 1. Can anyone help me with this? Thanks in advance!

``````#include <AccelStepper.h>

// Define the pins for each motor
#define X_DIR 5
#define X_STP 2

#define Y_DIR 6
#define Y_STP 3

#define Z_DIR 7
#define Z_STP 4

// Define the constants for the motors
#define X_SPEED 1000 // X steps per second
#define Y_SPEED 1000 // Y
#define Z_SPEED 1000 // Z

#define X_ACCEL 5000.0 // X steps per second per second
#define Y_ACCEL 5000.0 // Y
#define Z_ACCEL 5000.0 // Z

// Define the baud rate of the Arduino
#define BAUD_RATE 230400

// ---------------
// Motor variables

int motorID = 0;
int motors = {0,0,0};

AccelStepper stepper1(AccelStepper::DRIVER, X_STP, X_DIR);
AccelStepper stepper2(AccelStepper::DRIVER, Y_STP, Y_DIR);
AccelStepper stepper3(AccelStepper::DRIVER, Z_STP, Z_DIR);

AccelStepper steppers = {stepper1, stepper2, stepper3};

// -----------------------
// Communication variables

const byte buffSize = 64;

char inputBuffer[buffSize];
char mode[buffSize] = {0};
float value1 = 0.0;
float value2 = 0.0;
float value3 = 0.0;

boolean newDataFromPC = false;

// ----------------
// Action variables

float values;

//=================
// PUBLIC FUNCTIONS
//=================

// ----------------------------------------------------
// Function called once to initialise the Arduino board
void setup() {

// Initialise the baud rate of the Serial port
Serial.begin(BAUD_RATE);

// Set the initial max speed of the system
stepper1.setMaxSpeed(X_SPEED);
stepper2.setMaxSpeed(Y_SPEED);
stepper3.setMaxSpeed(Z_SPEED);

// Set the acceleration of the system
stepper1.setAcceleration(X_ACCEL);
stepper2.setAcceleration(Y_ACCEL);
stepper3.setAcceleration(Z_ACCEL);

// Set the Arduino as ready

}

// ---------------------------------------------
// Function called repeatedly to run the Arduino
void loop() {

// Get the data from the PC
getDataFromPC();

if(newDataFromPC){
executeThisFunction();
}

}

const char startMarker = '<';
const char endMarker = '>';

byte bytesRecvd = 0;

// -------------------------------------------------
// Read the serial port and extract commands from it
void getDataFromPC() {

// Check if data is coming through the serial port
if (Serial.available() > 0)
{

// Read the next incoming character

// Detect the end marker and call the parser - Needs to be 1st if statement
if (x == endMarker) {
newDataFromPC = true;

// Clear the buffer
inputBuffer[bytesRecvd] = 0;

return parseData();
}

// Save the current character in memory - Needs to be 2nd if statement

// Add the character to the buffer
inputBuffer[bytesRecvd] = x;
bytesRecvd ++;

// Correct if size is exceeding buffer size
if (bytesRecvd == buffSize) {
bytesRecvd = buffSize - 1;
}
}

// Detect the start marker and collect characters - Needs to be 3rd if statement
if (x == startMarker) {
bytesRecvd = 0;
}

}
}

// -----------------------
// Parse the incoming data
void parseData() {

char * strtokIndx; // this is used by strtok() as an index

// Extract the MODE
strtokIndx = strtok(inputBuffer, ",");
strcpy(mode, strtokIndx);

// Extract the MOTORID
strtokIndx = strtok(NULL, ",");
String motorstr(strtokIndx);

// Convert into an integer
motorID = atoi(strtokIndx);

// Enable the motors
toggleMotors(motorstr);

// Extract the VALUE for motor 1
strtokIndx = strtok(NULL, ",");
value1 = atof(strtokIndx);

// Extract the VALUE for motor 2
strtokIndx = strtok(NULL, ",");
value2 = atof(strtokIndx);

// Extract the VALUE for motor 3
strtokIndx = strtok(NULL, ",");
value3 = atof(strtokIndx);

// Set the "value 1-3" for array "values"
values = value1;
values = value2;
values = value3;

// Communicate back to the computer
newDataFromPC = true;

}

// Select the function to execute
void executeThisFunction() {

// Go to the SPEED setting function
if (strcmp(mode, "SPEED") == 0) {
}

// Go to the ACCEL setting function
else if (strcmp(mode, "ACCEL") == 0) {
updateAccel();
}

// Go to the RUN function
else if (strcmp(mode, "RUN") == 0) {
runFew();
}

}

void toggleMotors( String motorstr ) {

if(motorstr.indexOf("1") >= 0) {
motors = 1;
}
if(motorstr.indexOf("2") >= 0) {
motors = 1;
}
if(motorstr.indexOf("3") >= 0) {
motors = 1;
}
}

// Update the speed of the motor

switch (motorID) {

// Apply settings to motor 1
case 1:
stepper1.setMaxSpeed(value1);
Serial.println("update speed for motor 1");
break;

// Apply settings to motor 2
case 2:
stepper2.setMaxSpeed(value2);
break;

// Apply settings to motor 3
case 3:
stepper3.setMaxSpeed(value3);
break;
}

// Reset all variables
clearVariables();
}

// --------------------------------
// Update the accel of the motor
void updateAccel() {

switch (motorID) {

// Apply settings to motor 1
case 1:
stepper1.setAcceleration(value1);
Serial.println("update accel for motor 1");
break;

// Apply settings to motor 2
case 2:
stepper2.setAcceleration(value2);
break;

// Apply settings to motor 3
case 3:
stepper3.setAcceleration(value3);
break;
}

// Reset all variables
clearVariables();
}

// ------------------------------------
// Clear all variables after the action
void clearVariables() {

memset(mode,0,sizeof(mode));
motorID = 0;
value1 = 0.0;
value2 = 0.0;
value3 = 0.0;
}

// Run the motors to move the syringes
void runFew() {

AccelStepper steppers = {stepper1, stepper2, stepper3};

// Set the distance, and apply to the motor
for (int i = 0; i < 3; i += 1) {
if (motors[i] == 1) {
steppers[i].move(values[i]);
Serial.println("update move steps");
}
}

clearVariables();

// Initialise the motor status for the run loop
int stepperStatus = {0, 0, 0};

// Start the main loop
while (array_sum(stepperStatus, 3) != array_sum(motors, 3)) {

// Iteration over the 3 steppers
for (int i = 0; i < 3; i += 1) {

// Check if the stepper is selected
if (motors[i] == 1) {

// Ask the stepper to move to position at constant speed.
if (stepperStatus[i] == 0 ) {
steppers[i].run();
}

// Check if it reached it's position - if yes, set the status
if (steppers[i].distanceToGo() == 0) {
stepperStatus[i] = 1;
}
}
}

getDataFromPC();
if (strcmp(mode, "SPEED") == 0){
}
if (strcmp(mode, "STOP") == 0){
//return stopAll();
break;
}
}

Serial.println("Jump while");
clearVariables();
}

``````

You have a wait loop, waiting for all active motors to complete their motion before going back to look for more commands.

Put the stepperN.run() calls at the top of loop() so the stepper can do their thing while you look for more input. That will allow you to move (change the destination), change speed, or stop while the steppers are moving.

Another problem is the time it takes to run the loop() which will limit the maximum speed.
See the complete stepper example in my tutorial on [Multi-tasking in Arduino] (https://www.forward.com.au/pfod/ArduinoProgramming/RealTimeArduino/index.html) which shows you how to measure how fast your stepper can run and how to get it to run faster.
(Tip. call accelstepper.run() more often)
The stepper example is controlled by user input and a temperature sensor.

Hi, thanks for your reply. I am sorry that I didn't get your point. It's true that the condition of the while loop is waiting for all active motors to complete their motion (for the 3rd command, motor 1 should run 10000 steps). However, I called the getDataFromPC() inside the while loop to look for a new speed parameter. For example, if the Arduino receives the 4th command (<SPEED,1,1000,0,0>) at the end of the first iteration of the while loop, I think the speed should be set as 1000 during the second iteration of the while loop. Am I wrong? Could you please explain a little bit and give me more details?

What is the Serial Monitor saying is happening? When you send the "<SPEED,1,1000,0,0>" command, does it reply with: "update speed for motor 1"?

Could you put in some more Serial output, like logging the command received from the PC.

Hi John! Good to hear you again. Yes, after sending <SPEED,1,1000,0,0>, I can see "update speed for motor 1" from the serial monitor, which means stepper1.setMaxSpeed(1000) should have been executed. However, I can't see a speed change.

If I send <SPEED,1,1000,0,0> many times, I can also see these commands are received by arduino by observing the "update speed for motor 1".

Your sketch seems to be behaving correctly (after I added the missing array_sum() function). It looks like the AccelStepper library does not act on a change in stepper.setMaxSpeed() immediately. It doesn't even respond to stepper.setSpeed() immediately. There may some combination for library calls that will get your motor to change speed in the middle of a move, but I can't find it. I tried calling setSpeed() before and after setMaxSpeed() and that did not change anything. I know from the library sources that if the new value is the same as the old value the functions do nothing.

Good luck.

Hi John! I am sorry that I forgot to upload the array_sum() function. Thank you very much for debugging my code. Really appreciate your time.

Hi, thank you very much for pointing out this issue, and your tutorial is very helpful.

All speed changes are limited by the acceleration setting. Only the run() method actually updates the stepper steps.

You'd think that at an acceleration of 5000 it would not take long for the speed to change. The 10,000 steps starting at 100 steps per second and switching to 1000 steps per second always takes 100 seconds.

Can you come up with a way to get the speed to change before the move is complete? Of course it should be subject to acceleration.

AccellStepper is position controlled so that can interfere with your demanded speed.
If you want a speed control, with acceleration settings, try my Speed Control library
Stepper Speed Control Library project with plotting
This is still limited by position so you need to set the end stops well out of the way the get the speed.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.