New serial communication with serialport()

50 views (last 30 days)
Hi everyone,
I've updated to the new Release 2019b and found that the serial communication was completly updated. Before the update, I used a tag to find the serial port object in other functions so that I could divide the functions into several parts: setup of serial connection (open/close), sending input to the device (from different functions) and reading and processing the device response. But now, I don't see a way to access the serialport object created in the beginning in other functions.
What I want to accomplish is an interface to a arduino flushed with GRBL. Therefore, I need a way to constantly (every 0.5 seconds or so) updating the device status by querring a special real_time command which returns almost instantly the current instrument state (even while there is an other command beeing processed). In addition, I have to send commands, which usually give a positive respond as soon as their execution started.
So can I use the new configureCallback to change the callback function depending on whether I want to get the current device status or the status of my current command?
I was thinking about a buffer to store the commands send to the device and the response from the device so that I can keep track. The validation would occur as soon as we have a complete set (read and write operation done). Since the real time command is always executed and the response follows immediatly (before the response for other commands) I would place it on top of the buffer.
Thank you for your time!
function TestSerialCommunication()
% set up serial connection for the first time
sCon = serialport('COM3',115200);
configureTerminator(sCon,'CR/LF')
pause(2) % wait for instrument to get started
% flush buffer; remove instrument welcome message
flush(sCon);
% send homing cycle command in order to disable the alarm state
writeline(sCon,'$H')
% wait for 10 seconds to finish the homing cycle command
pause(10);
% device response is 'ok' as soon as homing cycle has been finished
count = sCon.NumBytesAvailable;
homingCycleStatus = read(sCon,count,'char');
disp(homingCycleStatus)
% configure timer object so that we can constantly update the instrument status
objTimer = timer('Tag','timerDeviceState',...
'BusyMode','drop',...
'ExecutionMode','fixedSpacing',...
'TimerFcn',@SendStatusRequest,...
'Period',0.5);
% now start timer
start(objTimer);
% for testing purposes we move the device around while we constantly
% update the device status via timer
% configure callback for movement commands
configureCallback(sCon,'terminator',@ResponseToMovement)
for n = 1:10
input = strcat('Y',num2str(n));
writeline(sCon,input) % tell the instrument to move 1 step on y-axis
end
% because there is no 'Tag' property for serialport() this function has
% to be a nested function of the main function which created the
% serialport object
function SendStatusRequest(~,~)
configureTerminator(sCon,'CR') % third argument is writterm
configureCallback(sCon,"terminator",@ResponseMaschineState)
% send status request to the instrument
write(sCon,'?','char')
end
end
% read the response from the device which was triggered by sending a status
% request command
function ResponseMaschineState(s,~)
% this function is only similar to the ResponseToMovement function for
% testing purpose. In reality they output is different and has to be
% processed in a different manner.
count = s.NumBytesAvailable;
if count > 0
data = read(s,count,'char');
disp(data)
end
end
% read the response from the device for the movement command
function ResponseToMovement(s,~)
% date = dataInfo.AbsTime;
count = s.NumBytesAvailable;
if count > 0
data = read(s,count,'char');
disp(data)
end
end

Accepted Answer

Guillaume
Guillaume on 18 Sep 2019
But now, I don't see a way to access the serialport object created in the beginning in other functions
You just have to pass the serial port object to your callback. One way:
objTimer = timer('Tag','timerDeviceState',...
'BusyMode','drop',...
'ExecutionMode','fixedSpacing',...
'TimerFcn',@(~, ~) SendStatusRequest(sCon), ... %just discard source and eventarg since you don't use them
'Period',0.5);
with non-nested function:
function SendStatusRequest(sCon)
configureTerminator(sCon,'CR') % third argument is writterm
configureCallback(sCon,"terminator",@ResponseMaschineState)
% send status request to the instrument
write(sCon,'?','char')
end
With the 2nd part of your question, is there any reason to handle data sent/data received separately? If you send a command, don't you have to wait for the full response before sending the next one? If so, there's no reason to do the communication asynchronously.
If you need to do asynchronous communication, then indeed sent and received buffer would be a way to handle this.
  2 Comments
Julian Schmid
Julian Schmid on 18 Sep 2019
Edited: Julian Schmid on 19 Sep 2019
Thank you very much for your answer. This is indeed a nice feature I haven't thought of... However, how would I get the serialport object into a function which is designed to handle the commands which shall be send to the instrument (for example via a button click) (lets call it SendToInstrument for now). Would it be suitable to call the SendToInstrument with an 'initialize' flag and the serialport object as function argument and store it as persistent variable? Is there a better way to do so?
I think I have to do the communication asynchronously because the instrument has a couple of buttons (abort, pause, continue) which interfere with the execution of the software.
/edit: another question came to my mind. What is the best way to wait for the instrument response but keep the gui responsive during this time? while s.NumBytesAvailible? waitfor and callback function of the serialport stores it in the userData property or something?
Guillaume
Guillaume on 19 Sep 2019
You can pass the serial port object to all functions that need to call SendToInstrument so that in turn they can pass it to the function. You could also use a persistent variable indeed, this wouldn't be the way I'd do it but it's certainly an option. Or you could wrap the code in a class, that may be what I'd do. There are many ways to achieve the same.
You certainly don't want while loops in a UI, so yes, you want to use the serial port callback. I'm not too clear on the full details of your application, can you send another command before receiving the full reply of the previous one? If not, you would have a can't send flag that would be set once you've sent a command and unset by the callback once the full reply has been received. If the flag is set, don't allow sending a new command. As to how you store flag, received data, etc. it's up to you. If you're using GUIDE the handle variable is probably best. A structure in userData another. Or again wrap it all in class. If you're using App designer, you would already have the class wrapper.

Sign in to comment.

More Answers (0)

Tags

Products


Release

R2019b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!