Stepper motor going too far after Serial Monitor

This is part of a more complicated program. I reduced it as much as possible. The point is to draw a shape with two stepper motors. The minimal code for only running one motor in the x-direction is below. So it just draws a line and goes back.

The goal is to be able to stop the motor, stop drawing the shape/line, and go back to the origin (starting position). It does this sometimes.

bool runIt = false;
const int dirPinX = 2;  // direction pin for motor
const int stepPinX = 3; // step pin for motor

volatile float deltaX = 0;
volatile float h = 0;   // theta
volatile float x1 = 0;
volatile float x2 = 0;
int stepCount = 0;


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

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
}

void loop()
{         
  if(runIt == false)
    circle();
}

void circle()
{
  float deltaH = PI/(20*200*4);

  while (h < 2*PI)
  {
    if(Serial.available() > 0)
    {
      goToOrigin();
      
      Serial.read();
      Serial.read();
      Serial.read();
      break;
    }

    x1 = cos(h)*20*200;
    x2 = cos(h + deltaH)*20*200;

    drawLocus();
    h += deltaH;
  }
}

void stepX()
{
  if (deltaX < 0) // Set motor direction clockwise
    digitalWrite(dirPinX, HIGH);

  else
    digitalWrite(dirPinX, LOW);


  digitalWrite(stepPinX, HIGH);
  delayMicroseconds(6);
  digitalWrite(stepPinX, LOW);
  delayMicroseconds(6);
  
  stepCount++;
  Serial.println(stepCount);
}



void drawLocus()
{
  deltaX += x2 - x1;
  if (abs(deltaX) > 2)
  {
    stepX();
    deltaX = 0;
  }
}






void goToOrigin()
{
  digitalWrite(dirPinX, LOW);

  for(int i = 0; i < stepCount; i++)
  {

    digitalWrite(stepPinX, HIGH);
    delayMicroseconds(6);
    digitalWrite(stepPinX, LOW);
    delayMicroseconds(6);
    
    stepCount++;
    Serial.println(stepCount);
  }
  
  h = 0;
  runIt = true;
}

Right now, it works for some small rotations. For larger rotations the stepper motors go further. It steps correctly. The mechanics are fine. I think it has something to do with how the Circle function or GoToOrigin function is being called.

Extra - attached are two programs that tested if the motor was not taking the correct steps. It takes the same number of steps ‘forward’ as putting the same variable ‘backward’ from the Serial Monitor of the first program. In other words, after a character was entered in the Serial Monitor to stop the stepper motor, the stepCount number was taken and put into the other program. For example, in the zResetToOrigin2 sketch, once any character is entered in the Serial Monitor, say stepCount ended at 3469. Then, in the goBack sketch 3469 was put for “amount”.

zResetToOrigin3.ino (1.5 KB)

goBack.ino (681 Bytes)

a single axis can't move in circles.

So please describe in normal words what kind of move shall the single axis do?

For moving in a two-dimensional area an algorithm called bresenham-algorithm is used.

You haven't written a question that is clearly asking for something.

best regards Stefan

Also please include short programs in your Post so we don’t have to down load them. Please use the code button </>. See How to use the forum

…R

I think for the type of program you have in mind you need to get away from using WHILE and FOR. Just use IF and allow loop() to do the repetition. That way it will be possible for a new instruction to be detected even in the middle of a move.

Have a look at how the code is organized in Several Things at a Time. Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

I would also suggest only having one function that actually moves the motor according to position values that can be calculated by specific functions - for example makeCircle() or moveToOrigin(). Always try to eliminate duplicate code as it is just an opportunity for errors.

The code in loop() might be something like this pseudo code

void loop() {
   receiveSerialData();
   if (instruction == 'C') {
     makeNextCirclePosition();
   }
   else if (instruction == 'O') {
    makeNextOriginPosition();
   }
   moveMotorToNextPosition();
}

...R
Serial Input Basics
Planning and Implementing a Program

The shortest version of the program is included following the forum’s guidelines. (The programs attached are extra.) No need to download anything.

There is one function to draw the circle. There are actually several shapes that use the same algorithm. The only duplicate code is this:

digitalWrite(stepPinX, HIGH);
  delayMicroseconds(100);
  digitalWrite(stepPinX, LOW);
  delayMicroseconds(100);

I don’t think I should create a function for this little bit. I think it’s clearer that way.

  1. But yes, I’m wondering about if some sort of function calling and break combination is causing it to not work correctly. (I used if-statements to the max. The stepper motors need a for-loop to run the desired number of steps.)
  2. Or that I put thSerial.available shebang in the wrong part of the program.
  3. Or maybe there is some mathematical reason it only needs 2000 steps–rather than 4000–to get back to the origin sometimes. That is still the major conceptual mystery. It runs from 4000 to -4000 forward and the reverse of that to run backward.

Minor note, I also wonder if I need multiple Serial.read(); in sequence. Does this “clean out the buffer”? I’m not sure. Anyway, today I’m just trying to functionally get it working by adding fudge factors:

bool runIt = false;
const int dirPinX = 2;  // direction pin for motor
const int stepPinX = 3; // step pin for motor

volatile float deltaX = 0;
volatile float h = 0;   // theta
volatile float x1 = 0;
volatile float x2 = 0;
int stepCount = 0;


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

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
}

void loop()
{         
  if(runIt == false)
    circle();
}

void circle()
{
  float deltaH = PI/(20*200*4);

  while (h < 2*PI)
  {
    if(Serial.available() > 0)
    {
      goToOrigin();
      break;
    }

    x1 = cos(h)*20*200;
    x2 = cos(h + deltaH)*20*200;

    drawLocus();
    h += deltaH;
    
//    Serial.print(x2);
//    Serial.print("\t");
//    Serial.print(h);
//    Serial.println();
  }
}

void stepX()
{
  if (deltaX < 0) // Set motor direction clockwise
    digitalWrite(dirPinX, HIGH);

  else
    digitalWrite(dirPinX, LOW);


  digitalWrite(stepPinX, HIGH);
  delayMicroseconds(100);
  digitalWrite(stepPinX, LOW);
  delayMicroseconds(100);
  
  stepCount++;
  Serial.println(stepCount);
}



void drawLocus()
{
  deltaX += x2 - x1;
  if (abs(deltaX) > 2)
  {
    stepX();
    deltaX = 0;
  }
}

void goToOrigin()
{
  Serial.println("Going to Origin");


  digitalWrite(dirPinX, LOW);

  int fudge = 1;
  int foo = 1;

  if(h < PI/2)            //        h < 1.57      4000 to 0      fwd
    foo = 1;
  if(1 < h && h < 2)
    foo = 4;
    
//  if(PI/2 < h && h < PI)  // 1.57 < h < 3.14    0 to -4000     fwd
//    fudge = 1;
//  if(PI < h && h < 2*PI)  // 3.14 < h < 4.71    -4000 to 0     back
//    fudge = 1; 
//  if(h > PI && h < 2*PI)  // 4.71 < h < 6.28    0 to 4000      back
//    fudge = 1;  

Serial.println(abs(2000-abs(x2)));

  for(int i = 0; i < fudge*abs(foo*2000-abs(x2)); i++)
  {    
    Serial.print("Entered for-loop, i: ");
    Serial.print(i);
    Serial.println();
    
    digitalWrite(stepPinX, HIGH);
    delayMicroseconds(100);
    digitalWrite(stepPinX, LOW);
    delayMicroseconds(100);
  }

    
  Serial.read();
  Serial.read();
  Serial.read();
  delay(5000);
  h = 0;
  runIt = true;
}

adamelli:
I don't think I should create a function for this little bit. I think it's clearer that way.

When you see the same code appearing in more than one place it should be in a function - no matter how short it is.

For solving your problem I refer you back to what I said in Reply #3

...R

It's just not possible to make if-statements for the for and while loops displayed in the minimal code snippet. Does not help.

The serial data is just an interrupt to the normal repetitive functioning. So if in the middle of making the shape, a character is entered and the void loop has the if-statement, it will complete the shape and stop. That defeats the whole point of the interrupt--which is to stop and return it back to the origin immediately. There would also be no use for a return-to-origin function if it was that simple because most of the shapes end up back at the origin.

Thank you for trying to help though.

The intended function is to stop whatever pattern is being made by the motors off some interrupt (such as any character being entered into the Serial Monitor). Then, whatever the position, to return back to the origin. Whatever locus is being made should not be completed.

adamelli:
It's just not possible to make if-statements for the for and while loops displayed in the minimal code snippet. Does not help.

It is possible - though it will take some time, a lot of changes will be needed.

And in my opinion it is essential.

...R

It's literally impossible to make the unknown amount of steps for different shapes for a stepper motor according to the algorithm with if-statements. Only using if-statements in 'void loop()' would also defeat the purpose of the interrupt.

  • Incorrect function calling and break combination?
  • Serial.available in the wrong part?
  • Math error - reason it only needs 2000 steps--rather than 4000? Actually, over 2000 steps it only goes halfway from the last code snippit, so it would be 4000 there...
  • Other

It's like a conversation I had. Another was discussing all about them getting a high amount of protein to lose weight. Losing weight has nothing to do with seeking a high protein intake. It doesn't help to eat a diet high in protein to lose weight. It doesn't help to use if-statements (and defeats the point).

The while and for loops are absolutely essential to the other functioning of the program.

adamelli:
It’s literally impossible to make the unknown amount of steps for different shapes for a stepper motor according to the algorithm with if-statements.

Sorry but that’s plain wrong.

Both of these pieces of code do the same thing (I hope :slight_smile: )

for (byte n = 0; n < 4; n++) {
   Serial.print("Number ");
   Serial.println(n);
}
void loop() {
   static byte n = 0;
   if (n < 4) {
     Serial.print("Number ");
     Serial.println(n);
   }
   n ++;
}

…R

Ya, I was wrong.

bool runIt = false;
const int dirPinX = 2;  // direction pin for motor
const int stepPinX = 3; // step pin for motor

volatile float deltaX = 0;
volatile float h = 0;   // theta
volatile float x1 = 0;
volatile float x2 = 0;
int stepCount = 0;
int i = 0;

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

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
}

void loop()
{         
  if(runIt == false)
    circle();
    
  else
    goToOrigin();

}

void circle()
{
  float deltaH = PI/(20*200*4);


  if(h < 2*PI && runIt == false)
  {
    x1 = cos(h)*20*200;
    x2 = cos(h + deltaH)*20*200;

    drawLocus();
    h += deltaH;
  }
}

void stepX()
{
  if (deltaX < 0) // Set motor direction clockwise
    digitalWrite(dirPinX, HIGH);

  else
    digitalWrite(dirPinX, LOW);


  digitalWrite(stepPinX, HIGH);
  delayMicroseconds(100);
  digitalWrite(stepPinX, LOW);
  delayMicroseconds(100);
  
  stepCount++;
  Serial.println(stepCount);
}



void drawLocus()
{
  if(Serial.available() > 0)
  {
    goToOrigin();
  }
    
  deltaX += x2 - x1;
  
  if(abs(deltaX) > 2)
  {
    stepX();
    deltaX = 0;
  }
}

void goToOrigin()
{

  digitalWrite(dirPinX, LOW);
  
//  Serial.print("x2: ");
//  Serial.println(x2);
//  Serial.println(4000-abs(x2));
  
  if( i < (4000-abs(x2)) )
  {    
    Serial.print("Entered i: ");
    Serial.print(i);
    Serial.println();
    
    digitalWrite(stepPinX, HIGH);
    delayMicroseconds(100);
    digitalWrite(stepPinX, LOW);
    delayMicroseconds(100);
  }
  else
    return;

  i++;

//  Serial.read();
//  Serial.read();
//  Serial.read();
//  delay(5000);
  h = 0;
  runIt = true;
}

Although, all the same issues still exist. It overshoots the origin a bit, if the steps are low (e.g., 500ish). It sometimes returns nearly to the origin (if steps taken under 1000, roughly). It sometimes overshoots, past the origin again (if steps taken beyond 4000 or when it is already on the return path). There are probably more instances where it under and overshoots. I mean, there are 0 to 4000 steps forward and then the same steps backward so a total of 8000 steps.

This code should not be in the drawLocus() function

  if(Serial.available() > 0)
  {
    goToOrigin();
  }

It has nothing whatever to do with drawing.

Put it in its own function and call it from loop()

…R

I did that, and over/undershoot persisted.

I put ‘stepCount’ instead of `2000 - abs(x2)’ and it half works. Once moving forward, it goes back to the origin. After starting to move backward it overshoots the origin. I’ll look at it longer.

bool runIt = false;
const int dirPinX = 2;  // direction pin for motor
const int stepPinX = 3; // step pin for motor

volatile float deltaX = 0;
volatile float h = 0;   // theta
volatile float x1 = 0;
volatile float x2 = 0;
int stepCount = 0;
int i = 0;

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

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
}

void loop()
{         
  if(Serial.available() > 0)
    goToOrigin();
  
  if(runIt == false)
    circle();
}

void circle()
{
  float deltaH = PI/(20*200*4);


  if(h < 2*PI && runIt == false)
  {
    x1 = cos(h)*20*200;
    x2 = cos(h + deltaH)*20*200;

    drawLocus();
    h += deltaH;
  }
}

void stepX()
{
  if (deltaX < 0) // Set motor direction clockwise
    digitalWrite(dirPinX, HIGH);

  else
    digitalWrite(dirPinX, LOW);


  digitalWrite(stepPinX, HIGH);
  delayMicroseconds(100);
  digitalWrite(stepPinX, LOW);
  delayMicroseconds(100);
  
  stepCount++;
  Serial.print("Steps: ");
  Serial.print(stepCount);
  Serial.println();
}



void drawLocus()
{
    
  deltaX += x2 - x1;
  
  if(abs(deltaX) > 2)
  {
    stepX();
    deltaX = 0;
  }
}

void goToOrigin()
{
  digitalWrite(dirPinX, LOW);


  if(runIt == false)
    delay(3000);
    

  if(i < stepCount && h <= PI)
  {
    digitalWrite(stepPinX, HIGH);
    delayMicroseconds(100);
    digitalWrite(stepPinX, LOW);
    delayMicroseconds(100);
    i++;
  }
  else if(i < stepCount && PI < h)
  {
    // math I can't think because I've been stairing at this for dayz
  }
  else
    return;

  h = 0;
  runIt = true;
}

In this code

if(i < stepCount && h <= PI)

Please don’t use single character variable names. When I tried to find where you defined i every single i in the program was highlighted.

…R

I’m more confused why the steps forward and back are 7103. Before it was a total of 8000. So before it was 4000 forward and 4000 back.

bool runIt = false;
const int dirPinX = 2;  // direction pin for motor
const int stepPinX = 3; // step pin for motor

volatile float deltaX = 0;
volatile float theta = 0;
volatile float x1 = 0;
volatile float x2 = 0;
int stepCount = 0;
int incr = 0;

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

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
}

void loop()
{         
  if(Serial.available() > 0)
    goToOrigin();
  
  if(runIt == false)
    circle();
}

void circle()
{
  float deltaTheta = PI/(20*200*4);


  if(theta < 2*PI && runIt == false)
  {
    x1 = cos(theta)*20*200;
    x2 = cos(theta + deltaTheta)*20*200;

    drawLocus();
    theta += deltaTheta;
  }
}

void stepX()
{
  if (deltaX < 0) // Set motor direction clockwise
    digitalWrite(dirPinX, HIGH);

  else
    digitalWrite(dirPinX, LOW);


  digitalWrite(stepPinX, HIGH);
  delayMicroseconds(100);
  digitalWrite(stepPinX, LOW);
  delayMicroseconds(100);
  
  stepCount++;
  Serial.print("Steps: ");
  Serial.print(stepCount);
  Serial.println();
}



void drawLocus()
{
    
  deltaX += x2 - x1;
  
  if(abs(deltaX) > 2)
  {
    stepX();
    deltaX = 0;
  }
}

void goToOrigin()
{
  digitalWrite(dirPinX, LOW);


  if(runIt == false)
    delay(1000);
    

  if(incr < stepCount && theta <= PI)
  {
    digitalWrite(stepPinX, HIGH);
    delayMicroseconds(100);
    digitalWrite(stepPinX, LOW);
    delayMicroseconds(100);
    incr++;
  }
  else if(incr < stepCount && PI < theta)
  {
    // math I can't think because I've been stairing at this for dayz
  }
  else
    return;

  theta = 0;
  runIt = true;
}

It looks like the function goToOrigin() starts counting from whatever value is in the variable incr. But the content of that variable does not seem to take account of where the motor may have got to as a result of another move.

It's only in Irish pub humour that "I wouldn't start from here" makes sense.

...R

void drawLocus()
{
    
  deltaX += x2 - x1;
  
  if(abs(deltaX) > 2)   // curmudgeon logic statement
  {
    stepX();
    deltaX = 0;
  }
}

For the drawLocus function, it makes sense that it doesn't need the 4000. Putting in 2000 only sometimes looked like it worked, but was really just getting close the majority of instances (sweet spot).

First 5 steps:
0 4000.00
1 3997.98 2.02
2 3995.96 2.02
3 3993.96 2.00
4 3991.91 2.05
5 3989.90 2.01

So that solves the 2000 vs 4000 conceptual issue. Why it's taking only 7103 steps instead of 8000, I think, because of the decimal places (where x2 is float and the steps are integer).

I fixed the program so that it works in the x-direction for a circle. Although, other more complex shapes... gonna take some ingenuity...

Variable/function names are 'lower_case' and 'separated_with_underscores'
Named constants are in 'ALL_CAPITAL_LETTERS'
Classes are in 'CamelCase'

Maybe I should do this instead of putting everything in camel case.

adamelli:
For the drawLocus function, it makes sense that it doesn't need the 4000. Putting in 2000 only sometimes looked like it worked, but was really just getting close the majority of instances (sweet spot).

I can't make any sense of that - and especially not in the context of my Reply #16.

Variable/function names are 'lower_case' and 'separated_with_underscores'

I use camelCase for almost everything. Typing an underscore needs a third hand :slight_smile:

...R