BytesAvailableFcn callback not being triggered in GUI
5 views (last 30 days)
Show older comments
I have a GUI I have set up that needs to receive, act on and display data received from packets being sent from a server.
In the GUI ...OpeningFcn I have the following code:
%start up communication
handles.socket = tcpip('127.0.0.1', 1025, 'NetworkRole', 'client');
handles.socket.BytesAvailableFcnCount = 10;
handles.socket.Timeout = 120;
set(handles.socket.BytesAvailableFcn, {@socketCallBack, handles}); %have also tried handles.socket.BytesAvailableFun = {@socketCallBack, handles}
fopen(handles.socket);
get(handles.socket,'BytesAvailableFcn')
handles.socketData = uint8([]);
guidata(hObject, handles);
lastcommand = 'Opening socket to robot';
append_commandlog(hObject, handles, lastcommand); %function that writes to a commandlog displayed in the GUI
% Check if the connection is valid.
if(~isequal(get(handles.socket, 'Status'), 'open'))
warning(['Could not open TCP connection to 127.0.0.1 on port 1025']);
return;
end
lastcommand = 'Connection accepted';
append_commandlog(hObject, handles, lastcommand)
And I have a callback function as follows:
function socketCallBack(hObject, eventdata, handles)
handles = guidata(hObject);
a = fread(socket, socket.BytesAvailable);
disp(a)
lastcommand = sprintf('Recieved a message from robot: %s', char(data));
append_commandlog(hObject, handles, lastcommand)
guidata(hObject, handles);
I know that the socket has connected, and that data is being recieved, as printing out handles.socket in the callback function of the button prints
TCPIP Object : TCPIP-127.0.0.1
Communication Settings
RemotePort: 1025
RemoteHost: 127.0.0.1
Terminator: 'LF'
NetworkRole: client
Communication State
Status: open
RecordStatus: off
Read/Write State
TransferStatus: idle
BytesAvailable: 117
ValuesReceived: 0
ValuesSent: 0
However, the callback function does not ever seem to be triggered, as the commandlog stays unchanged, and nothing relating to the socket is ever printed to the terminal. What is going on?
0 Comments
Answers (3)
Benjamin Avants
on 2 Nov 2017
I know this question is a little old but just in case it is still troubling you or for anyone else who comes across it, I will answer it anyways.
Walter's answer is partially correct and you did need to change the BytesAvailableFcnMode to 'bytes'.
However you have a bigger problem in the callback function itself:
function socketCallBack(hObject, eventdata, handles)
handles = guidata(hObject);
a = fread(socket, socket.BytesAvailable);
disp(a)
lastcommand = sprintf('Recieved a message from robot: %s', char(data));
append_commandlog(hObject, handles, lastcommand)
guidata(hObject, handles);
when a tcpip, serial, or similar object triggers a callback in Matlab the first argument passed is always the handle to the communication object. These objects are not graphics objects so the guidata function will not work with them. The function would error on the very first line and the BytesAvailableFcn would be disabled (usually this prints a warning to the command window).
The next problem is that you would not be reading data from the socket even if you got to the second line. In this case, the socket would be either handles.socket or hObject, as both variables should be pointing to the same thing. Then there's the variable 'data' that is used seemingly in error in place of the 'a' variable.
Looking past the errors in the code, I would advise not using handles for storing or passing anything that is either large or frequently changing. If handles contains a lot of data it can dramatically slow down a GUI as every callback function accesses the entire structure when it is called. Another problem is one that shows up in your code; the version of handles passed to the tcpip callback is stale and will not contain any updated information. If you must use handles then pass guidata(hObject) as the additional variable to the callback and it will receive the current structure every time it is called.
Instead, you should use the handles structure for its namesakes (grpahics and object handles) only and then use either getappdata and setappdata or else someObject.UserData to store and pass data and information. The appdata approach lets you set however many variables of whatever type you want to any object that you want, and it is more efficient and responsive than the handles structure since you can query and set one thing at a time. The UserData approach only allows storage of a single variable, but it can be a struct (like handles) or cell array... allowing for more information to be stored and accessed than it would seem at first. One upside of this approach is the ability to set listeners that trigger callback functions any time the UserData property is set. This allows you to have simple asynchronous functions listening for incoming data and just storing it in some object's UserData which can then trigger other functions to react to the new data. Written well this can increase responsiveness quite a lot.
2 Comments
Walter Roberson
on 2 Nov 2017
This is good information, but I think the part
" If you must use handles then pass guidata(hObject) as the additional variable to the callback and it will receive the current structure every time it is called."
could use some clarification. You cannot
set(handles.socket.BytesAvailableFcn, {@socketCallBack, guidata(hObject)})
as hObject is not defined at that point. If you instead code
set(handles.socket.BytesAvailableFcn, {@socketCallBack, guidata(gcf)})
then you have the problem that the guidata() will be evaluate once at the time that the callback is being set and would be stale after that.
More robust would be
fig = gcf; %or adjust according to your knowledge of a variable that is holding the figure information
set(handles.socket.BytesAvailableFcn, {@socketCallBack, fig})
and change
function socketCallBack(hObject, eventdata, handles)
handles = guidata(hObject);
to
function socketCallBack(hObject, eventdata, fig)
handles = guidata(fig);
Benjamin Avants
on 13 Nov 2017
Thanks for the comments, Walter.
Your approach is certainly clearer and more robust than what I described in that section of my answer. I'm fairly certain that I have had that syntax work for me before, but perhaps the version of handles I was actually passing was stale and I didn't realize it.
One thing I don't think I mentioned (and that would be easier to deal with using your approach) is the need to validate the handle before calling guidata. In the case of an unexpected error it can be important for callbacks raised by objects that aren't children of the GUI figure (like sockets, timers, or serial objects) to test for the validity of any handles before using them, and perhaps even attempt to safely close the source of the callback when an invalid handle is detected.
Walter Roberson
on 22 Aug 2017
You did not set https://www.mathworks.com/help/instrument/bytesavailablefcnmode.html BytesAvailableFcnMode to 'bytes'
2 Comments
Francisco Naranjo
on 21 Mar 2018
Hi, did you ever found a solution to this issue; I'm running with the same problem
Francisco Naranjo
on 13 Mar 2018
Hello Gentlemen, I currently having the same issue and I tried the same code example as describe here but the same problem…. the callback function does not ever seem to be triggered. Is there a functional code example that can be provided so I can understand the whole functionality? Thanks a bunch I would really appreciated
this is what I have:
handles.socket =tcpip('localhost',6340,'NetworkRole', 'client');
handles.socket.BytesAvailableFcnMode = 'terminator';
handles.socket.Terminator = 'LF';
handles.socket.ReadAsyncMode = 'continuous';
fig = gcf;
set(handles.socket.BytesAvailableFcn, {@socketCallBack , fig})
fopen(handles.socket);
get(handles.socket,'BytesAvailableFcn')
handles.socketData = uint8([]);
guidata(hObject, handles);
My Function :
function socketCallBack(hObject, eventdata, fig)
handles = guidata(fig);
a = fread(handles.socket, socket.BytesAvailable);
disp(a)
lastcommand = sprintf('Recieved a message: %s', a);
append_commandlog(hObject, handles, lastcommand)
guidata(hObject, handles);
6 Comments
Walter Roberson
on 22 Mar 2018
I would wonder whether the data being sent is indeed being terminated by LF.
Have you put on a network snooper to verify that data is being sent from 6340 towards MATLAB, and verify that it does have LF terminators ?
See Also
Categories
Find more on Containers in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!