Sending Data over Serial Interrupted by Stepper

Hi,

Will try to explain my application. The application is a measuring device, having a measuring probe, and a stepper motor. The work sequence is basically as follows:

Request data from probe->
-> Send data to Excel ->
-> Rotate motor by given step->
-> Request new data from probe ->
-> Send data to Excel-> ..... and so on...

I am using Arduino Leonardo for this. It communicates to the probe over Serial1 and communicates with a PC (Excel) over Serial2 port. MS Excel talks through serial port using a plugin called PLX-DAQ (see Download - Parallax for details).

Problem is, if you check the code, you will see there are 3 lines commented out. This are the commands to run the motor. If I enable these lines, then all works (and the motor rotates), but there is no data passed to Excel. I am getting blank cells. I am not sure why it could be. I am guessing it is some timing issue, but cannot see what it is. With this lines commented out there is data passed to Excel. The library I am using to control the stepper is called AccelStepper (here's some info: AccelStepper Arduino Library, connecting Stepper Motors to Teensy).

Anyone, any thoughts? Appears like the stepper.run() command is not getting finished before data is being sent to Excel...?

Thanks,
Dan

 int row = 0;
  int Step = 111;
  char command, data ;
  char value[7]; //
 
 #include <AccelStepper.h>
 AccelStepper stepper(1,5,7); // Step pin #5, DIR pin #7
 
void setup() {
 
  Serial.begin(128000); // opens serial port, sets data rate to 128000 bps
  while (!Serial) {
    ; // wait for serial port to connect.
  }
   Serial1.begin(1200, SERIAL_7E1);
 
  //================ RELAY SETUP ============//
  pinMode (12,OUTPUT); //power supply to relay
  digitalWrite(12,HIGH); //relay board ON
  pinMode (13, OUTPUT);  // relay signal
  digitalWrite(13,HIGH); // relay OFF
  //========================================//
 
  //============= Stepper Setup ===========//
  pinMode (6, OUTPUT); // Stepper DISABLE pin
  digitalWrite(6, LOW); // Disable stepper
 
   stepper.setMaxSpeed(100000);
   stepper.setAcceleration(100000);   
   stepper.setSpeed(100000);
   stepper.setMinPulseWidth(5);
  //======================================//
 
 
}

void loop() {
  command = 'V';
  Serial.println("CELL,GET,Z3"); // Read command from Excel
  delay(5);
  command = Serial.read();
  Serial.read();
  Serial.read(); //empty buffer
 
  if ((command == 'a') || (command == 'b') || (command == 'c')) // check whether first, second or third measurement is requested
  {
    digitalWrite(6, HIGH); // activate stepper
    Serial.println("ROW,SET,2"); //set row 2 in Excel as first row to print
    delay(2);
   
    for(row=0; row < 90; row++)
    {
     digitalWrite(13,LOW); //activate relay - request data from probe
     int value_place = 0; //string pointer
     delay(50);
     for(value_place =0; Serial1.available()>0; value_place++) // while data available - byte at a time as single character
     {
      delay(20);
      value[value_place] = Serial1.read();
      delay(1);
     }
     digitalWrite(13,HIGH); //deactivate relay
     Serial.print("DATA,"); Serial.println(value); // print value to Excel
     //stepper.move(Step);  // define stepper move size
     //stepper.runToPosition(); // Define stepper movement type: move stepper by Step. Wait until it's moving
     //stepper.run(); // Actual move command of stepper
     delay(10);
         
    }
   
    Serial.print("CELL,SET,Z4,"); Serial.println(command);
    digitalWrite(6, LOW); // deactivate stepper
  }
  Serial.println("CELL,SET,Z3,V");
   
delay (100);

}

ExcelStepper.ino (2.29 KB)

I think the problem is stepper.runToPosition() which the documentation says is a blocking command. I don't know whether you need stepper.run() at all.

You should be able to organize your code without using runToPosition() by making it move one step in every iteration of loop() using other accelStepper commands.

...R

Hi,

Thanks for the reply. Omitting stepper.RunToPosition() no movement occurs. Same with stepper.run().

Moving one sterp per iteration of the loop() is no good for me as I need to move a given distance each time I measure.

Dan

Sorry, you were right. The stepper.run() command is redundant. The stepper rotates without it as well:

 int row = 0;
  int Step = 111;
  char command, data ;
  char value[7]; //
 
 #include <AccelStepper.h>
 AccelStepper stepper(1,5,7); // Step pin #5, DIR pin #7
 
void setup() {
 
  Serial.begin(128000); // opens serial port, sets data rate to 128000 bps
  while (!Serial) {
    ; // wait for serial port to connect.
  }
   Serial1.begin(1200, SERIAL_7E1);
 
  //================ RELAY SETUP ============//
  pinMode (12,OUTPUT); //power supply to relay
  digitalWrite(12,HIGH); //relay board ON
  pinMode (13, OUTPUT);  // relay signal
  digitalWrite(13,HIGH); // relay OFF
  //========================================//
 
  //============= Stepper Setup ===========//
  pinMode (6, OUTPUT); // Stepper DISABLE pin
  digitalWrite(6, LOW); // Disable stepper
 
   stepper.setMaxSpeed(100000);
   stepper.setAcceleration(100000);   
   stepper.setSpeed(100000);
   stepper.setMinPulseWidth(5);
  //======================================//
 
 
}

void loop() {
  command = 'V';
  Serial.println("CELL,GET,Z3"); // Read command from Excel
  delay(5);
  command = Serial.read();
  Serial.read();
  Serial.read(); //empty buffer
 
  if ((command == 'a') || (command == 'b') || (command == 'c')) // check whether first, second or third measurement is requested
  {
    digitalWrite(6, HIGH); // activate stepper
    Serial.println("ROW,SET,2"); //set row 2 in Excel as first row to print
    delay(2);
   
    for(row=0; row < 90; row++)
    {
     digitalWrite(13,LOW); //activate relay - request data from probe
     int value_place = 0; //string pointer
     delay(50);
     for(value_place =0; Serial1.available()>0; value_place++) // while data available - byte at a time as single character
     {
      delay(20);
      value[value_place] = Serial1.read();
      delay(1);
     }
     digitalWrite(13,HIGH); //deactivate relay
     Serial.print("DATA,"); Serial.println(value); // print value to Excel
     //stepper.move(Step);  // define stepper move size
     //stepper.runToPosition(); // Define stepper movement type: move stepper by Step. Wait until it's moving
     delay(10);
         
    }
   
    Serial.print("CELL,SET,Z4,"); Serial.println(command);
    digitalWrite(6, LOW); // deactivate stepper
  }
  Serial.println("CELL,SET,Z3,V");
   
delay (100);

}

As far as I understand, the program should wait until stepper.runToPosition() command is executed and only then proceed, but it seems not to....

Any thoughts?

Dan

dan13:
As far as I understand, the program should wait until stepper.runToPosition() command is executed and only then proceed, but it seems not to....

I'm a bit confused now. I thought it was runToPosition() that was interfering with your other code? Now you seem to be saying it doesn't work at all? Does the stepper move?

You can write your code so that it will execute all the steps one at a time (the Arduino is so fast that it will seem continuous) on each iteration of loop() while also doing other things like sending/receiving serial data. You just need to use variables to record the state of different processes so that you don't start something when it should not.

The demo several things at a time should help to illustrate the idea.

...R

You are right. It is the runToPosition() which is interfering. What I meant to say in the previous post is that it does execute this command, but seems to skip to the next on before it's finished. And so the serial data isn't being printed.

Thanks for the exmaple. It seems to be a good way of doing things. Don't see how I can implement it though. Here is the list of all commands of AccelSteper:

http://www.airspayce.com/mikem/arduino/AccelStepper/classAccelStepper.html#a176c5d2e4c2f21e9e92b12e39a6f0e67

noticed something interesting: using the runToNewPosition(Step) command, the data is passed to Excel fine, but no movement of stepper since in this case Step is an absolute value. But as soon as I increment the Step in the loop (Step += 111), no data is passed....

Dan

I may not get back to this until tomorrow.

...R

It looks to me as if the stepper motor is being driven correctly and your call to stepper.runToPosition() should move the stepper by the number of steps you specified and block until the stepper reaches that position (the call to run() is redundant but harmless). I think you're saying that the stepper is moving correctly but perhaps you can confirm that.

I think the problem is that the serial output you're expecting isn't being sent. This code looks like a disaster waiting to happen and probably accounts for that problem:

     delay(50);
     for(value_place =0; Serial1.available()>0; value_place++) // while data available - byte at a time as single character
     {
      delay(20);
      value[value_place] = Serial1.read();
      delay(1);
     }

I think you need to add code to receive the response from the probe and wait until you have received the complete response, and not just assume that the data you expected arrived when you expected it.

Could the high currents associated with the stepper motor be causing interference
on the serial line?

You haven't shown your circuit.

Hi Peter,

PeterH:
It looks to me as if the stepper motor is being driven correctly and your call to stepper.runToPosition() should move the stepper by the number of steps you specified and block until the stepper reaches that position (the call to run() is redundant but harmless). I think you're saying that the stepper is moving correctly but perhaps you can confirm that.

This is correct. The stepper is moving fine. And it also does so with the run() command omitted. When the runToPosition() executes and blocks, doesn't it mean that the next command should wait for it to end first?

PeterH:
I think the problem is that the serial output you're expecting isn't being sent. This code looks like a disaster waiting to happen and probably accounts for that problem:

     delay(50);

for(value_place =0; Serial1.available()>0; value_place++) // while data available - byte at a time as single character
     {
      delay(20);
      value[value_place] = Serial1.read();
      delay(1);
     }

When I request for data from the probe, it sends character by character. So, for instance, a value of "-0.482" would consist of 6 characters. The the above snippet is the only idea I had in order to write it all to a single variable which I can then pass to Excel.

PeterH:
I think you need to add code to receive the response from the probe and wait until you have received the complete response, and not just assume that the data you expected arrived when you expected it.

This takes care of it:

for(value_place =0; Serial1.available()>0; value_place++)

Hi Mark,

MarkT:
Could the high currents associated with the stepper motor be causing interference
on the serial line?

You haven't shown your circuit.

I believe it is not the case here, as I would see some junk output in case of electrical noise. But I am not seeing anything.

Dan

Hi Guys,

Any other thoughts on this? Still can't get it to work...

Thanks,
Dan

did you try Serial.parseFloat()?

void setup() 
{
  Serial.begin(9600); 
}
void loop() 
{
  if (Serial.available())
  {
    float myFloat = Serial.parseFloat();
    Serial.println(myFloat, 4);
  }
}

set the timeout with Serial.setTimout(myTime) the default is 1000milliseconds

Hi,

I didn't try this. I don't see how it applies here. I am dealing with characters, not numbers...

Dan

sorry I read this:

probably accounts for that problem:

Code:
delay(50);
for(value_place =0; Serial1.available()>0; value_place++) // while data available - byte at a time as single character
{
delay(20);
value[value_place] = Serial1.read();
delay(1);
}

When I request for data from the probe, it sends character by character. So, for instance, a value of "-0.482" would consist of 6 characters. The the above snippet is the only idea I had in order to write it all to a single variable which I can then pass to Excel.

I assumed you were trying to retrieve a float

Assuming we are still talking about the code in Reply #3 then this code has a lot wrong with it

    for(row=0; row < 90; row++)  {
       digitalWrite(13,LOW); //activate relay - request data from probe
       int value_place = 0; //string pointer
       delay(50);
       for(value_place =0; Serial1.available()>0; value_place++) { // while data available 
          delay(20);
          value[value_place] = Serial1.read();
          delay(1);
       }
       digitalWrite(13,HIGH); //deactivate relay
       Serial.print("DATA,"); Serial.println(value); // print value to Excel
       //stepper.move(Step);  // define stepper move size
       //stepper.runToPosition(); // Define stepper movement type: move stepper by Step. Wait until it's moving
       delay(10);
         
    }

First, it seems to be reading 90 items of data and trying to run the stepper 90 times. That has to be sorted out. When, exactly, is the stepper supposed to move?

Second, it has a very primitive way of getting data using Serial1..read(). There is no check to see if reading the data starts in the correct place or ends in the correct place. There is no check to see if correct data is received. There is no check to prevent too much data being received which might corrupt the memory space for other variables.

The demos here and here may help to illustrate how to receive and parse data.

...R

Hi Robin,

Thanks for the valuable information. I will study it. I am not a programmer of any kind, but find it very interesting to tackle such projects. Thanks for the patients and help.

Yes, we are still talking about the code you referred to. You are correct, it requests for and reads 90 items of data. After reading each value the stepper moves to the next location and the next value is read, and so on 90 times. Do you think I am trying to move it in the wrong time in the code?

Dan

dan13:
Do you think I am trying to move it in the wrong time in the code?

Taking account of the earlier discussion about accelstepper I got the impression your code wants to move it the full distance 90 times.

The better way to use AccelStepper is to set the number of steps to be moved and then call motor.run() in every iteration of loop().

However, if each iteration of loop() tries to get 90 items of data that's not going to work very well. So the solution has to involve change to both parts. You need to just read one item of data for each iteration of loop() and also just do one step of the motor.

The second of the 2 demos I linked to (using Python, etc) shows how to read one item from serial in each iteration of loop. When you have that sorted the stepper motor stuff should fit in easily.

It may help to think of things like this -- loop() repeats regularly so why do you need another repetitive system (FOR) to collect data from serial1. It's a bit like hiring a truck to carry your car around for you.

...R

Hi Robin,

Thanks for explaining this.

I think I don't clearly understand the sequence in which the Arduino executes the code. The Void() loop is executed over and over again at the clock frequency of the microcontroller. Then if I have another loop inside Void(), what happens with it? Are you suggesting that it enters that loop and doesn't move on until the loop terminated, and the Void() loop execution frequency is affected?

Otherwise I don't understand what's the difference between calling functions in the main loop and actually executing the code in the main loop. Isn't it the same timing-wise?

Dan

The Arduino can only do one thing at a time in strict order. I will try to explain it with a diagram

void loop() {
A
B
C
for (3 times) { // repeats for as many times as you specify
X, Y, Z
} // now it carries on with the next step in loop()
D
E
} // and now it goes back to A again

A single C/C++ instruction usually gives rise to several (maybe dozens of) Microprocessor instructions and most of the microprocessor instructions take one cycle of the 16MHz clock to execute (some take 2). The time it takes for loop() to get back to the start depends on how much stuff has to be done. And if the MCU is diverted by one or several interrupts, the time taken to deal with them also counts.

...R

OK. So what is the difference between my code and the way you suggest doing it by calling functions to do things? Both take same clock time, don't they?

Dan