Problem with controlling stepper motor using serial inputs

Hello,

For the last week or so I've been working on controlling a 6 wire stepper motor using Arduino. I'm using an Arduino UNO R3 and an Arduino motor shield.

I've based my code to receive the serial inputs on Robin2's code found on this page: Serial Input Basics - Programming Questions - Arduino Forum

In my code (please see below), I have several additional functions so that different commands can be executed while the motor is moving. Typing commands like or should give feedback, and I also want it to give an "error" message when an invalid command is sent. However, I have a problem occurring where inputting valid commands while the motor is moving sometimes returns the error message - and this happens randomly, but usually when I try to do or for the first time (it works the second time). It also works flawlessly some times but not others.

Now, this is a weird case and so may be an unanswerable one, but I appreciate any time you can spend helping.

Many thanks!

Adam

// Code to control the stepper motor, using serial port inputs

//========= For motor control ==========
#include <Stepper.h>                  // Arduino stepper library

const int stepsPerRev = 200;          // Set number of steps per revolution, depending on motor
Stepper motor1(stepsPerRev, 12, 13);  // Initialise the stepper library on the motor shield

const int pwmA = 3;             // Name the control pins
const int pwmB = 11;
const int brakeA = 9;
const int brakeB = 8;
const int dirA = 12;
const int dirB = 13;


//========== For communication ==========
const byte numChars = 90;
char receivedChars[numChars];
char cancelNow;
boolean newData = false;
int total = 0;
int steps = 0;
char* input = 0;
int onOff = 0;


//========== SETUP ==========
void setup() {
  Serial.begin(9600);

  pinMode(pwmA, OUTPUT);
  pinMode(pwmB, OUTPUT);
  pinMode(brakeA, OUTPUT);
  pinMode(brakeB, OUTPUT);
  digitalWrite(pwmA, HIGH);
  digitalWrite(pwmB, HIGH);
  digitalWrite(brakeA, LOW);
  digitalWrite(brakeB, LOW);
  onOff = 1;

  motor1.setSpeed(30);

  motor1.step(3);
  
  Serial.println("<Arduino is ready>");
}


//========== MAIN LOOP ==========
void loop() {
  recvChars();
  processCommand();
  controlWait();
}


//========== FUNCTIONS ==========

void recvChars() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';                // To mark the start and end of the data
  char endMarker = '>';
  char rc;

  while (Serial.available() > 0 && newData == false) {    // While loop to collect serial data
    rc = Serial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {        // If the character is not the end marker
        receivedChars[ndx] = rc;    // Stores the character in an array
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars -1;
        }
      }
      else {
        receivedChars[ndx] = '\0';  // Terminates the string if the character is the end marker
        recvInProgress = false;     // Resets the progress
        ndx = 0;
        newData = true;
      }
    }
    else if (rc == startMarker) {   // Doesn't store the start marker
      ndx = 0;
      recvInProgress = true;        // and sets progress to true to enable previous if statement
    }
  }
}


void processCommand() {             // Function to move the stepper motor  
  if (newData == true) {            // If a command was received
    input = receivedChars;          // Stores the command in the input variable
    
    totalSteps();                   // Calls the following functions to find required action
    motorPower();
    stopMotor();
    motorDirection();
    resetSteps();
    motorStatus();
    doSteps();
  }
}


void doSteps() {
  steps = atoi(receivedChars);      // Convert ASCII characters to Integer (atoi)
  newData = false;
  input[0] = '\0';
  if (steps > 0) {                  // If the integer is positive (clockwise rotation)
    while (steps > 0) {             // A loop to allow further inputs while moving
      input[0] = '\0';
      receivedChars[0] = '\0';
      motor1.step(1);               // Does 1 step every loop
      steps--;                      // Decreases number of steps left to do
      total++;                      // Increases the total number of steps taken
      recvChars();                  // Calls the function to check if any other inputs
      input = receivedChars;        // Stores the input

      stopMotor();                  // Calls the following functions to see if action is required
      totalSteps();
      motorDirection();
      motorStatus();
      
      if (input[0] != '\0') {       // If the input hasn't been reset yet, display error
        Serial.println("error");
      }
      
      newData = false;              // Resets command status so new input can be accepted
      input[0] = '\0';
    }
  }
}


void stopMotor() {
  if (strcmp(input, "stop") == 0) {       // Checks if input was "stop"
    steps = 0;                            // Sets steps to 0 to stop the motor
    Serial.println("Stopping");
    input[0]='\0';                        // Terminates the string
    newData = false;                      // Resets the command status
  }
}


void motorPower() {
  if (strcmp(input, "on") == 0) {         // Checks if input was "on"
    initialise();                         // Calls the initialise function to power on
    Serial.println("Motor on.");
    input[0]='\0';                        // Terminates the string
    newData = false;                      // Resets the command status
  }
  else if (strcmp(input, "off") == 0) {   // Checks if input was "off"
    standby();                            // Calls the standby function to power off
    Serial.println("Motor off.");
    input[0]='\0';
    newData = false;
  }
}


void totalSteps() {
  if (strcmp(input, "total") == 0) {      // Checks if input was "total"
    Serial.println(total);
    input[0]='\0';                        // Terminates the string
    newData = false;                      // Resets the command status
  }
}


void motorDirection() {
  if (strcmp(input, "direction") == 0) {  // Checks if input was "direction"
    if (steps > 0) {
      Serial.println("The motor is moving clockwise.");
    }
    else if (steps < 0) {
      Serial.println("The motor is moving anticlockwise.");
    }
    else {
      Serial.println("The motor is not moving.");
    }
    input[0]='\0';                        // Terminates the string
    newData = false;                      // Resets the command status
  }
}


void resetSteps() {
  if (strcmp(input, "reset") == 0) {      // Checks if input was "reset"
    total = 0;
    Serial.println("Total number of steps has been reset.");
    input[0]='\0';                        // Terminates the string
    newData = false;                      // Resets the command status
  }
}


void motorStatus() {
  // No code yet...trying to fix problems first
}


void controlWait() {
  delay(20);
}


void initialise() {       // Sets the pins high to engage motor
  digitalWrite(pwmA, HIGH);
  digitalWrite(pwmB, HIGH);
  onOff = 1;
}


void standby() {          // Sets the pins low to stop motor
  digitalWrite(pwmA, LOW);
  digitalWrite(pwmB, LOW);
  onOff = 0;
}

I like Robin's idea of doing stuff in functions. I no NOT like the idea of the function needing to decide whether it should do something.

I would use:

void loop()
{
  recvChars();
  if(newData)
  {
    processCommand();
    controlWait();

    newData = false;
  }
}

This way, processCommand() needs to know nothing about newData.

In fact, I'd go one step further, and make recvChars return a boolean that I'd assign to a static variable, newData, in loop(), and get rid if it as a global variable.

  onOff = 1;

Now, there is a really crappy name. Suppose we have a boolean variable called motorSpinning. Suppose that we set that to true. Then, suppose that we have some code:

if(motorSpinning)
{
   // Stop it
   motorSpinning = false;
}

Now, it's real clear what motorSpinning means, and what motorSpinning being true means, vs. what motorSpinning being false means. What the hell does onOff equal 1 tell us? Not a damned thing.

Does onOff equal 1 mean on? Or does it mean off?

  if (newData == true) {            // If a command was received

If newData contains true, the test is true == true, which is true. If newData contains false, the test is false == true, which is false. So, it is clear that the == true part of that statement is useless. Look like a real programmer, and get rid of it.

    totalSteps();                   // Calls the following functions to find required action
    motorPower();
    stopMotor();
    motorDirection();
    resetSteps();
    motorStatus();
    doSteps();

So, how to you expect to receive new commands, like or <dammit, stop right now!>?

I don't see where processCommands() actually does any processing. So, the name leaves a bit to be desired, doesn't it?

Type anything into the serial monitor, between < and > tags, and the same set of functions is going to be called, right?

const int pwmA = 3;             // Name the control pins
const int pwmB = 11;
const int brakeA = 9;
const int brakeB = 8;

PWM and BRAKE have nothing to do with stepper motors. Are you actually using a stepper motor ? The code in your initialise() and standby() functions is also not appropriate for a stepper.

I don't understand how you are trying to use your control system. You just seem to be sending words to the Arduino that cause different messages to be printed but you do not seem to be sending any data - such as the number of steps to move ?

Rather than call a series of functions (any one of which may find a suitable message) I think you should check for a message and then call the relevant function.

if (strcmp(input, "stop") == 0) {   
    // doStop();
  else   if (strcmp(input, "on") == 0) {  
    // doOn();
  else ....

...R

PaulS:
I like Robin's idea of doing stuff in functions. I no NOT like the idea of the function needing to decide whether it should do something.

I strongly support your right to a different opinion.

However I think that expressing it here is more likely to confuse the OP than to assist him.

...R

Thanks both for the feedback.

The stepper motor has 200 steps per revolution. With the code, you can type in, say, <2000> to make it do 10 revolutions. While it is doing this, I wanted to be able to input things like to abort the rotation, or to get the total steps moved so far. Only those specific commands would be accepted, otherwise an error should be returned.

@PaulS:

I will try your suggestion to improve the loop function. I've had very limited experience with Arduino programming, and so that may show in my code.

Yes, 'onOff' is very unclear - I had planned to improve that but got stuck working around the problem described above instead.

Type anything into the serial monitor, between < and > tags, and the same set of functions is going to be called, right?

The functions that are called should check for the input - so they won't all happen each time. I wanted it to check these every time round the loop.

@Robin2:

Yes, I am using a stepper, but was unsure of how to get it to move - I looked up various other options but that didn't seem to control it correctly.

The code in your initialise() and standby() functions is also not appropriate for a stepper.

Do you have any suggestions on how to do this more appropriately?

I don't understand how you are trying to use your control system. You just seem to be sending words to the Arduino that cause different messages to be printed but you do not seem to be sending any data - such as the number of steps to move ?

If you type in <200>, the motor will move 200 steps by looping motor1.step(1) 200 times. While it is in the loop, I wanted it to check for other inputs so that you can stop it if needed. The other things, like and simply give feedback on the current status of the motor. The problem is that the error message is sometimes displayed instead.

In my attempts to solve the problem, I think I did try checking for the message before calling the function, but I will try this again and tidy things up.

I think that you will need a state machine approach. On every pass through loop(), see if there is a new command to implement. If so, decide if that needs to be implemented now, as stop should be, or after the current command has finished. If now, change the current command (and dump the queue). If not, add it to a queue.

Decide which function to call, based on the current command. That function can decide whether it needs to do anything, or if it is done. If it needs to do something, it should then determine if it is time to do it (if time matters). if there is no longer anything to do, pop that command off the stack.

An empty stack means that there is nothing to do.

As an example, suppose you send <200>. The loop() function will probably iterate 100,000 times while that data arrives. Only when the > arrives do you know that 200 has arrived. On each of those 100,000 iterations, the stack is empty, so there is nothing (else) to do.

The 200 arrives, and gets pushed on the queue. The loop() function iterates, and sees that there is no serial data pending, so it looks at the queue, and sees that there is something to do, so it calls the appropriate function, which causes the motor to take ONE step, and returns a value saying that it is not done.

The loop() function ends, and gets called again. No serial data, so another step happens. This repeats until there is serial data, saying stop, or until all 200 steps have been taken. When all the steps are done, the function returns done, so 200 is removed from the queue.

Next time loop() gets called, there is no serial data, and the queue is empty, so it does nothing.

Does this make sense? Do you have a relatively good idea how you would implement it? The only hard part is the queue management, which isn't all that hard. I think that there is even a queue library for the Arduino (or perhaps it's a stack library).

Yes, that's a very good explanation of how I would like the system to work. I've not heard of using queues before, but I will search for some examples. There seems to be both a queue and a stack library - would one be more appropriate than the other?
Queue library page: Arduino Playground - QueueList Library
Stack library page: Arduino Playground - StackList Library

I'll re-write a lot of my code and try to figure out how to implement the queue. I assume you can just pass the string that is received to the queue and it will execute it when it reaches the top? And do a test for "stop" which will empty the stack and return done to stop the motor?

would one be more appropriate than the other?
Queue library page: Arduino Playground - QueueList Library

The queue is more appropriate. As commands come in, they get in line (a queue). When the Arduino is ready, it takes the first command in the queue. The first command in is the first out.

A stack is a LIFO thing - the last command in would be the first out. Probably not what you want.

Great, thanks a lot for the help!

adambl:
Yes, I am using a stepper, but was unsure of how to get it to move - I looked up various other options but that didn't seem to control it correctly.
Do you have any suggestions on how to do this more appropriately?

Sorry, I forgot to include links to stepper motor basics and this simple stepper code.

...R

Great, thank you for the links Robin - I'll take a look at that example and see if I can make it work with my motor/shield.