Arduino not properly following instructions over Serial port

Hello,

I work in a Radar lab and currently I am working on an XY positioner/scanner that takes X and Y coordinates and moves an antenna to those coordinates. It is probably a little over 1 square meter (the XY space).

Anyway, the scanner uses stepper motors to move the antenna. The XY coordinates are entered in Matlab and the data is sent to the Arduino via a Serial communication. The code first executes a function called “SetToHome()” which essentially zeros the scanner at (0,0). This is done in the void setup() section. In the void loop() section, the Arduino reads the data from Matlab (a vector consisting of the X and Y steps, as well as a delay value between points) and executes the functions MoveXMot() and MoveYMot() which will send the pulses to the stepper motors to move the antenna.

The SetToHome() function will ALWAYS work. The problem is after this, in the loop. The scanner will do one of three things:
1. Not move at all
2. Move the wrong amount, but consistently. For example, when I tell it to move 1 inch, it will move 2.5 inches. When I tell it to move 2 inches, it will move 3.5 inches.
3. Move the wrong about, but differently from 2. It will move about 30 inches when I tell it to move 1.

I have no idea why this is happening. Attached is the Matlab and Arduino code to examine. (Ignore the MoveRotMot function and the value of “z” in Matlab. There used to be a 3rd motor that would rotate the antenna but this is not important right now. I need to get only the XY working.)

One thing I realized is I could not print many things to the serial monitor. For example, I wanted to see the value of stepsX to see how Arduino is reading the data from Matlab and it wont print. If I put a print message in the setup section it will print, and in other random places like during the while loop. But most of the time I cannot get any data to print.

I figure the problem HAS to be the serial communication. What else would cause the Arduino to not send the correct number of steps???

Thanks for any help!!

EDIT: I couldn’t attach the matlab file. Let me know if this is needed and I will find a way to provide a link to it.

MATT2.ino (4.8 KB)

Post your code using code tags.

It would help to see the matlab code but your Arduino code already has errors.
In each for loop you do this sort of thing:

  for( int k; k <=(stepsX); k++)

which allocates a new variable called ‘k’ on the stack but does not initialize it. So you will turn the motor a random amount each and every time depending upon whatever random value happened to be on the stack.
In this case you should use:

  for( int k = 1; k <=(stepsX); k++)

which will execute stepsX steps. I will leave it as an exercise for you to figure out how to fix the for loop which handles the case where stepsX is negative.

Pete

Here is the Arduino Code:

int xpinClk = 6;
int xpinDir = 8;
int ypinClk = 5;
int ypinDir = 7;
int zpinClk = 4;
int zpinDir = 9;
int pinStop = 2;
int StepsX;
int StepsY;
int StepsZ;
int PauseBtwPoints;
int targetStepsX;
int targetStepsY;
int targetStepsZ;
int xPos = 0;
int yPos = 0;
int zPos = 0;
int xInc;
int yInc;
int zInc;
int y_last = 0;
int y_move;
int endStop;
int MoveDelay = 400; //This controls the speed of the scanner.

void setup() {
  pinMode(xpinClk,OUTPUT);
  pinMode(xpinDir,OUTPUT);
  pinMode(ypinClk,OUTPUT);
  pinMode(ypinDir,OUTPUT);
  pinMode(zpinClk,OUTPUT);
  pinMode(zpinDir,OUTPUT);
  pinMode(pinStop,INPUT);
  digitalWrite(pinStop,HIGH);
  Serial.begin(9600);
  Serial.setTimeout(10);
  Serial.flush();
  SetToHome();
}


void loop() {
  //This is the main code that will run for each coordinate.
    while(Serial.available() < 1){ //Wait for a data point to be sent over serial
    }
    Serial.print("after while");
    StepsX = Serial.parseInt(); //X value in steps
    Serial.print(StepsX);
    StepsY = Serial.parseInt(); //Y value in steps
    StepsZ = Serial.parseInt(); //Z value in steps
    PauseBtwPoints = Serial.parseInt(); //Delay in milliseconds
    targetStepsX = StepsX - xPos; //Calculate target X value using current X position.
    targetStepsY = StepsY - yPos; //Calculate target Y value using current Y position.
    targetStepsZ = StepsZ - zPos; //Calculate target Z value using current Z position.

   MoveXMot(targetStepsX);
   MoveYMot(targetStepsY);
   Serial_flush();
}


void MoveRotMot(int steps)
{
 int k = 1;

 if(steps > 0){
   digitalWrite(zpinDir,HIGH);
   for( k; k <=(StepsZ); k++)
   {
      
      digitalWrite(zpinClk,HIGH);
      delayMicroseconds(MoveDelay);
      digitalWrite(zpinClk,LOW);
      delayMicroseconds(MoveDelay);
    } 
 }
  else{
    if(steps < 0){
      digitalWrite(zpinDir,LOW);
      for( k; k >=(StepsZ); k--)
      {
        digitalWrite(zpinClk,LOW);
        delayMicroseconds(MoveDelay);
        digitalWrite(zpinClk,HIGH);
        delayMicroseconds(MoveDelay);
      }
      digitalWrite(zpinClk,LOW);
    }
  }
}


void Serial_flush(){
  while(Serial.available()>0){
    Serial.read();
  }
  delay(PauseBtwPoints); //Delay to give positions time to settle.
  Serial.println(1); //Print value to line telling MATLAB to continue
}

void SetToHome(){
  //Set X Home
  endStop = digitalRead(pinStop);
  Serial.print(pinStop);
  digitalWrite(xpinDir,LOW);
  while(endStop != 1){
      endStop = digitalRead(pinStop);
      digitalWrite(xpinClk,HIGH);
      delayMicroseconds(MoveDelay); 
      digitalWrite(xpinClk,LOW);
      delayMicroseconds(MoveDelay);
  }
  digitalWrite(xpinClk,LOW);
  digitalWrite(xpinDir,HIGH);
  delay(500);
  for(int j=1;j<=50;j++){
      digitalWrite(xpinClk,HIGH);
      delayMicroseconds(MoveDelay);
      digitalWrite(xpinClk,LOW);
      delayMicroseconds(MoveDelay);
  }
  digitalWrite(xpinClk,LOW);
  delay(1000);
  xPos = 0;
  //Set Y home
  endStop = digitalRead(pinStop);
  digitalWrite(ypinDir,LOW);
  while(endStop != 1){
      endStop = digitalRead(pinStop);
      digitalWrite(ypinClk,HIGH);
      delayMicroseconds(MoveDelay);
      digitalWrite(ypinClk,LOW);
      delayMicroseconds(MoveDelay);
  }
  digitalWrite(ypinClk,LOW);
  digitalWrite(ypinDir,HIGH);
  delay(500);
  for(int l=1;l<=50;l++){
      digitalWrite(ypinClk,HIGH);
      delayMicroseconds(MoveDelay);
      digitalWrite(ypinClk,LOW);
      delayMicroseconds(MoveDelay);
  }
  digitalWrite(ypinClk,LOW);
  delay(1000);
  yPos = 0;
}

void e_STOP(){
  digitalWrite(xpinClk,LOW);
  digitalWrite(ypinClk,LOW);
  while(1){
  }
}

void MoveXMot(int stepsX)
{
   // endStop = digitalRead(pinStop);
  //if(endStop == 1){
   // e_STOP();
   // }
  if(stepsX > 0){
   digitalWrite(xpinDir,HIGH);
   for( int k; k <=(stepsX); k++)
   {
      
      digitalWrite(xpinClk,HIGH);
      delayMicroseconds(MoveDelay);
      digitalWrite(xpinClk,LOW);
      delayMicroseconds(MoveDelay);
    } 
 }
  else{
    if(stepsX < 0){
      digitalWrite(xpinDir,LOW);
      for( int k; k >=(stepsX); k--)
      {
        digitalWrite(xpinClk,LOW);
        delayMicroseconds(MoveDelay);
        digitalWrite(xpinClk,HIGH);
        delayMicroseconds(MoveDelay);
      }
      digitalWrite(xpinClk,LOW);
    }
  }
}

void MoveYMot(int stepsY)
{
    endStop = digitalRead(pinStop);
  if(endStop == 1){
    e_STOP();
    }
  if(stepsY > 0){
   digitalWrite(xpinDir,HIGH);
   for( int k; k <=(stepsY); k++)
   {
      
      digitalWrite(ypinClk,HIGH);
      delayMicroseconds(MoveDelay);
      digitalWrite(ypinClk,LOW);
      delayMicroseconds(MoveDelay);
    } 
 }
  else{
    if(stepsY < 0){
      digitalWrite(ypinDir,LOW);
      for( int k; k >=(stepsY); k--)
      {
        digitalWrite(ypinClk,LOW);
        delayMicroseconds(MoveDelay);
        digitalWrite(ypinClk,HIGH);
        delayMicroseconds(MoveDelay);
      }
      digitalWrite(ypinClk,LOW);
    }
  }
}

And here is the Matlab Code:

%Clear command window and workspace
clc
clear all

%Determine data entry mode
disp('RUNNING XYZ MODE');
mode = input('Please enter 0 for manual entry or 1 to read a file containing [x y z delay] data in inches and milliseconds.\n\n');

if mode == 0
    x = input('Please enter x value inches.\n\n');
    y = input('Please enter y value in inches.\n\n');
    z = input('Please enter z value in degrees.\n\n');
    delay = 0;
    position_data = [x,y,z,delay];
    rows = 1;
elseif mode == 1
    %Loads a text file with a user interface and saves the data into 
    %a variable called 'data'. Thanks to Mike Robinson for the code for this
    %section
    [filePath,pathName] = uigetfile('.txt'); %Get file path and path name
    position_data = dlmread(strcat(pathName,filePath)); %open file
    delay = position_data(:,4);
    [rows, n] = size(position_data);
else
    disp('Please enter 0 for manual entry or 1 to read a file\n\n')
end

%Define the limits in inches of the scanner
x_limit = 35.6;
y_limit = 35.6;

%Find the indices of values that are outside of the scanner bounds
ix = find(position_data(:,1) > x_limit);
iy = find(position_data(:,2) > y_limit);

%Find the x and y values that are outside of the scanner bounds
bad_x_vals = position_data(ix,1);
bad_y_vals = position_data(iy,2);

if ((ix ~= 0) | (iy ~=0)) %If there is at least one value outside of the range
    for c = 1:length(ix) %Display x-values and indices that exceed range
        disp(['Your x values of ' num2str(bad_x_vals(c)) ' inches with index ' ...
               num2str(ix(c)) ' are out of the ' num2str(x_limit) ' inch range. Please revise.'])
    end
    for c = 1:length(iy) %Display y-values and indices that exceed range
        disp(['Your y values of ' num2str(bad_y_vals(c)) ' inches with index ' ...
               num2str(iy(c)) ' are out of the ' num2str(y_limit) ' inch range. Please revise.'])
    end
    error('Exiting m-file') %Exit the m-file35
else %If no values are outside the range
    disp('All values are within bounds of scanner!') %Let the user know the scan will proceed
end

%Convert position data to step information
stepsPerInch = 500; %Define scanner resolution
stepsPerDegree = 67;
step_data_x = stepsPerInch*position_data(:,1); %Convert x direction to steps
step_data_y = stepsPerInch*position_data(:,2); %Convert to steps
step_data_z = stepsPerDegree*position_data(:,3); %Convert to steps

%Add in delay column
Arduino_data = [step_data_x step_data_y step_data_z delay];

s = serial('COM4','BaudRate',9600); %Establish serial with COM6, Arduino's serial port, with Baud rate 9600.
set(s,'InputBufferSize',512); %Establish a 512 byte buffer to read data back from Arduino.
fopen(s); %Open serial connection.
pause(2); %Wait 2 seconds for Arduino to reset after opening serial connection.

for k = 1:rows %For the number of positions to go to
    x_value = num2str(Arduino_data(k,1)); %Write the x-value of that position as a string.
    y_value = num2str(Arduino_data(k,2)); %Write the y-value of that position as a string.
    z_value = num2str(Arduino_data(k,3)); %Write the z-value of that position as a string.
    delay_value = num2str(Arduino_data(k,4)); %Write the y-value of that position as a string.
    fwrite(s,strcat(x_value,';',y_value,';',z_value,';', delay_value,';')); %Combine x, y and z together, separated by ;'s.
    while(s.BytesAvailable == 0) %While the buffer is empty (no data has been sent back)
    end %Continuously wait (infinite loop).
    fgets(s); %Gets ok signal from Arduino to proceed.
end

fclose(s); %Close the serial port.

Thanks for the replies so far! I will work on fixing the for loop.

el_supremo:
It would help to see the matlab code but your Arduino code already has errors.
In each for loop you do this sort of thing:

  for( int k; k <=(stepsX); k++)

which allocates a new variable called ‘k’ on the stack but does not initialize it. So you will turn the motor a random amount each and every time depending upon whatever random value happened to be on the stack.
In this case you should use:

  for( int k = 1; k <=(stepsX); k++)

which will execute stepsX steps. I will leave it as an exercise for you to figure out how to fix the for loop which handles the case where stepsX is negative.

Pete

OMG THIS MADE IT WORK!! Thank you so much! I have been trying to fix this forever! Wow simple fix!