Quicker way to serial print to Matlab

Hello All,

I am controlling a stepper motor with an Arduino, and I have a camera simultaneously taking images of an object that the motor is moving. I would like to send the current position of the stepper motor (currently being counted in the variable “Distance”) every time I take a snapshot in Matlab. Unfortunately when I run this program with the Serial.println line in the stepper code, the motor slows down tremendously as it spends time sending the serial info to MATLAB.

Is there a better way to send serial information from the Arduino without noticeably affecting my motor function? Or keep the information in a buffer so that it can be quickly sent only when I use the fread code from Matlab?

Arduino code:
#include <AccelStepper.h>
#include <MultiStepper.h>


int STEPS=17000;
int dir = 0;
void setup() {                
  pinMode(8, OUTPUT);     
  pinMode(9, OUTPUT);
  digitalWrite(8, LOW); //High=forward, low=backward
  digitalWrite(9, LOW);
  Serial.begin(9600);           // set up Serial library at 9600 bps
}

void loop() {
  //menu(); //call the menu
  while (!Serial.available()){ //wait until a serial value is available from MATLAB
  
  }
  parsemenu(Serial.read()); //decide what to do
}
//void menu(){
//  //decide what to do
//  //use serial command in arduino window for this first test
//  Serial.println("What should I do?");
//  Serial.println("enter b for move back, enter f for move forward");
//}

void parsemenu(char c){
  switch (c) {
  case 101:
  move_forward();
  break;
  case 100:
  move_back();
  break;
  default:
  break;
  }
}

void move_forward() {
  digitalWrite(8, HIGH); //conveyor moves forward
  for (int Distance = 0; Distance <STEPS; Distance++){ //move a certain number of steps
  digitalWrite(9, HIGH);
  delay(1); //delay between steps (ms)         
  digitalWrite(9, LOW); 
  delay(1); //delay between steps (ms)
  Serial.println(Distance);
}
}
void move_back() {
  digitalWrite(8, LOW); //Conveyor moves back
  for (int Distance = STEPS; Distance >=0; Distance--){ //move a certain number of steps
  digitalWrite(9, HIGH);
  //digitalWrite(11, HIGH);
  delay(1); //delay between steps (ms)         
  digitalWrite(9, LOW); 
  //digitalWrite(11, LOW);
  delay(1); //delay between steps (ms)
  Serial.println(Distance);
  }
}
Matlab code:
clear all
close all
clc

%% Lets define some variables
steps = 1; %Number of milliseconds per step (higher is slower) 200 steps per revolution
n = 0; %step counter
d = 16.9/1000; %distance per step [cm]
phi = (.978*pi())/1000*(180/pi()); %rotation per step [deg]
startang = 0; % first point to check around circle
stepsintest =  17000; %number of steps that the conveyer will travel

%% Lets open up the camera
 
vid = imaq.VideoDevice('dcam', 1, 'Y8_640x480');
%src = getselectedsource(vid);
vid.DeviceProperties.ShutterControl = 'absolute';
vid.DeviceProperties.ShutterAbsolute = 0.04;
release(vid);

%% Start a serial connection with the Arduino
s=serial('/dev/cu.usbmodemFA131','BAUD', 9600); 
% Make sure the baud rate and COM port is same as in Arduino IDE
fopen(s);
%% Send one frame through algorithm to establish baseline angle
current = double(round(255*step(vid)));
imageheight=size(current,1);
imagewidth=size(current,2);
% Take a first image to define the properties
[startang, ~] = angle_centroid(current,imageheight,imagewidth,startang);

% Create a matrix with actual values of angles based 
[~,reference] = meshgrid(1:2,0:stepsintest);
reference(:,1)=reference(:,1).*phi;
reference(:,1)=reference(:,1)+startang;

%% Start collecting data
for m=1:2 %do this twice
distance = 0;
servalue= input('Enter the value 101 to move forward 100 to move backward:');
fprintf(s,servalue);          %This command will send entered value to Arduino 
i = 1;
while (distance < (stepsintest-5))
    distance = fread(s,'%d');
    current = double(round(255*step(vid)));
    i=i+1;
    pause(0.1);
    lastdistance(i)=distance;
    
end
end

fclose(s);

You can increase the baudrate to something that both side understand. Not sure what matlab can do, but

Serial.begin(115200);

will increase the baudrate to 115200.

At 9600 baud a character takes roughly 1ms. Worst case you’re sending 7 characters (values > 10000, 5 characters for the digits plus cr/lf). Although the Serial.println is interrupt driven, your for loop will fill up the serial tx buffer because your sending more data per 2 milliseconds than can be moved out. And I suspect therefore that Serial.println starts blocking because the buffer is full.

You can also consider to send the data in binary form instead of ascii; but in this case I think that the overhead that is required to be able to synchronize the 2 bytes of the integers will not give much advantage.

Alternative:
send a command to the Arduino to get the ‘distance’ at the moment that your matlab application takes a snapshot.

Note:
why use 100 and 101 in the switch/case in parseMenu? Would it not be a lot clearer to use ‘d’ and ‘e’?

Thank you sterretje

I am trying to implement your alternative, I will update with how that goes.

And cheers for the note, the 100 and 101 were remnants from some testing I did earlier...

get the Serial.println(Distance) out of the for() loops. Don't need to show every step it makes.

blimpyway:
get the Serial.println(Distance) out of the for() loops. Don't need to show every step it makes.

I wonder about that :wink: As far as I understand it, OP wants to know the 'exact' position when a photo is taken.

Taking it out of the for loop will also make the Distance variable go out of scope and once that is solved will
always give the same value. It will take a bit more effort. One other solution might be to only send every 10 steps (or so).

instead of fixed STEPS constant global var should either implement "f 205" and "b 1034" to make whatever number of forward/backward steps he needs, or to have "s numSteps" command to update the STEPS when he needs to.


if he wants single step resolution feedback then can speed it a bit by writing back only one character per step e.g. one "F" to notify matlab a step was done forward and a "B" for each backward step.

I assume matlab has enough processing capabilities to count bytes.


anyway the code is in infancy, normally should have control not only on range of movement but also on its speed.

I ended up actually triggering the camera based on the linear position of the carriage as suggested by Sterretje.

sterretje:
One other solution might be to only send every 10 steps (or so).

I used Serial.write to send a single byte of data every N number of steps, which does not interrupt the normal function.

Thanks all for your help.