0

I am trying to make a program to control a stepper motor with serial inputs, and in writing a function to jog it, I am having trouble figuring out how to make a serial input stop the loop that steps the motor without making the time per step inconsistent. Is there a way where I can have a new serial input act like an interrupt and break any loop that's in progress.

void jog() {
  if(input[1] == 0) sd.setDirection(0);
  else sd.setDirection(1);
  while(true){
    if(Serial.available() > 0) break;
    sd.step();
    delayMicroseconds(10);
  }
  sd.setDirection(0);
}

Right now I have this (I'm using a Pololu library that defines the steps), but for some reason this does nothing or at least moves so slow it's imperceptible (a single step only moves the motor 0.007 degrees). It moves the motor very smoothly if I delete if(Serial.available() > 0) break; but then I can't stop it.

void loop() {
  getData();
  switch(input[0]){
    case 'r':
      Serial.write('r');
      ramp();
      break;
    case 'c':
      Serial.write('c');
      cycle();
      break;
    case 'j':
      //Serial.write('j');
      jog();
      break;
    default:
      break;
  }
}
void getData(){
if(Serial.available() > 0){
  for(int i = 0; i < 200; i ++){
    input[i] = 0;
  }
  for(int i = 0; i < Serial.available(); i++){
    input[i] = Serial.read();
    Serial.print(input[i]);
  }
}
Serial.flush();
//Serial.print(input[0]);
}

This is the relevant code that calls jog()

  • Please show us a complete working example, that shows your problem, not only one function. Can it be, that there is still data on the Serial buffer, when you enter the jog() function? In that case it would exit directly – chrisl Dec 21 '19 at 8:31
  • It does not have anything in the buffer when it starts. Post updated with full code. – Bob Dave Dec 21 '19 at 8:45
0

How do you know, that there is nothing in the Serial buffer, when you enter the jog() function? You don't check for this.

I think your problem is actually in your getData() function. The for loop

for(int i = 0; i < Serial.available(); i++){

might not work as you think, because i increases in every loop iteration while Serial.available() decreases in every iteration. So you actually read less data, than is available on the buffer. For example let's assume, that you have 2 Bytes on the buffer. Serial.available() returns 2. In the first for loop iteration i is 0, while Serial.available() is 2. When you read from the Serial buffer in the iteration, Serial.available() decreases to 1 (since you removed one byte from the buffer by reading it). At the end of the iteration, the for loop will increase i by 1 (due to the i++). Now in the condition of your for loop, you have 1 < 1 (because both i and Serial.available() are 1), thus the for loop will exit without any further iterations. But you still have 1 Byte left in the buffer, that you didn't read.

As I read from the documentation of Serial.flush() (though the documentation is notoriously bad) and from this question, I see, that it will only wait until the output buffer is emptied by the interrupt routines. It does nothing with the input buffer. So you still have data in your RX buffer after this line.

That means, that you really have data in your RX buffer, when you get into the jog() function. Thus it will directly exit without doing any step.


To solve the issue, you should first correct your getData() function to read all the available data from the RX buffer. You can change your for loop like this:

for(int i = 0; Serial.available() > 0 && i < INPUT_BUFFER_SIZE; i++){

This will loop until the RX buffer is empty (when Serial.available() returns 0) or until your input[] array is full (Please add a constant or a define for the input buffer size. Maybe you already have it. You didn't include the full code. But I guess it is 200).

After the for loop, you can empty the buffer by reading from it, until there is nothing left (Please see the linked question for that). You can even do this at the start of your jog() function. This ensures, mostly, that no data is in the buffer at the start of the jog() function (mostly, because the timing of data reception could be, that you receive a byte between the emptying of the RX buffer and the loop. Little probability, but not impossible).

Though - as Nick Gammon states in the linked answer - it is not the best way to empty the RX buffer and use every serial input as interrupt (sometimes you don't have exact control, when exactly the data is received). A better solution is to implement a message protocol, where the end of a full message is marked with a special character. Then you read from serial, until a full message is received (you can even do this non-blocking, so that you can do other things until the message is complete), and only then act upon the message. This would need bigger changes in your code. You would have to rewrite your getData() function and then remove all blocking code from the sketch (for example all delays) and work with non-blocking code instead (for example using the micros() function. The principle is explained in the BlinkWithoutDelay example, that comes with the Arduino IDE (though it uses millis() instead of micros() it is the same principle). You would call the jog() function repeatedly, which would only do a step, if it is time to do one. In the meantime the rest of the code can receive new messages. This makes you more flexible and independent of the exact timing of data reception. Since this would be a big rewrite of your code, you have to decide yourself, if you want to go this way, or stay with the simpler and more imperfect solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.