USB callback "terminator" fails to trigger a read when messages are close together

9 次查看(过去 30 天)
My App Designer program uses USB communication to an Arduino
It usually works but every once in a while Matlab fails to read.
configureCallback(app.PicoCom,"terminator",@app.PicoInput);
...
function PicoInput(app,src,~)
% check if there is more than 1 line. For some reason I don't get another callback!!!
while (app.PicoCom.NumBytesAvailable>0)
raw = readline(src);
I've already added the while loop which mostly fixed it except if the messages are 20msec apart.
The only time it fails (and not very often) is between 2 messages that are known to be 20 ms apart.
When it fails (App Designer says "Run" ... no action), I can go into the debugger (with a special pause I put in to read app properties) and check with
K>> app.PicoCom
ans =
Serialport with properties:
Port: "COM6"
BaudRate: 57600
NumBytesAvailable: 11
K>> read(app.PicoCom,11,"char")
ans =
'z73851718
'
And there it sits, not being read.
Sure, there are probably work arounds (I'm open to suggestions to manage it now) but isn't this a bug!
My next attempt to handle this is to add a pause(0.03); at the end of the while loop but this is very unsatisfying and how do I know it will NEVER fail?

采纳的回答

Gavin
Gavin 2024-9-23

更多回答(1 个)

Matlab Pro
Matlab Pro 2024-9-15
Here I am sednig to pieces of code:
1): simple example where you just wait for NumBytesAvailable to get some posistive value, read the data and thats all..
2): reading expected # of bytes:
Are you handling some kind of protocol with the Arduino?
I believe that the answer is 'yes', so the protocol should have some bytes pointing to the expected data length.
In the example below: the expected dataLength given in bytes #3 and #4
After caculateding the expeced dataLength - the code cotiues reading (with a minimal delay) till goal is achieved
Good luck @Gavin!
% 1) Reading while NumBytesAvailable = 0
switch class(portObj)
case 'internal.Serialport'
write(portObj,data_write,"uint8");
while (portObj.NumBytesAvailable == 0)
pause(0.01);
end
out = read(portObj,portObj.NumBytesAvailable,"uint8");
end
% 2) Reading expected # of bytes (BY PROTOCOL)
switch class(portObj)
case 'internal.Serialport'
write(portObj,data_write,"uint8");
while (portObj.NumBytesAvailable == 0)
pause(0.01);
end
out = read(portObj,portObj.NumBytesAvailable,"uint8");
% If you expect that some of the data still can not be read
% (delayed data)
dataLength = out(3)*256+data(4);
% Some data is still delayed on channel...
while length(out) < dataLength
while (portObj.NumBytesAvailable == 0)
pause(0.01);
end
tmp = read(portObj,portObj.NumBytesAvailable,"uint8");
out = [out,tmp];
end
end
  2 个评论
Gavin
Gavin 2024-9-15
编辑:Walter Roberson 2024-9-15
Thanks for trying but it's not so simple. BTW you can't call pause(x) in this kind of program as it stops everything!
There is no "expected number of bytes"
It's a complicated program where MatLab App Designer GUI is telling Pico what to do for the next trial and then logging the results. I need to use the 'terminator' version of callback as once Pico gets the Go signal it takes many seconds for the trial to run depending on the dats it is acquiring from sensors and actions it takes based on the command given. Meanwhile, there are too many possible interactions with the user to have any main line code. The whole point of Apps is that they can run and actions (methods) are triggered by callbacks. What is happening is that the Pico initiated (CRLF) callbacks don't happen, especially not in the debugger.
It's now in a situation where it's not working at all at a certain point and eventually MatLab itself crashes (if I go Continue,and not break in the debugger. I can't even stop the program or quit MatLab without CTRL-ALT-DEL End task !!
Using break as each trial starts (just be for I send Pico another Go command) should work since an entire trial is processed before the next one gets called and I get to the Go again. But MatLab isn't reading and processing the inputs before starting the next trial:
Look how chars have piled up, there are no replies longer than 20 characters so this buffer of NumBytesAvailable is way behind where it should be.
K>> app.PicoCom
ans =
Serialport with properties:
Port: "COM6"
BaudRate: 57600
NumBytesAvailable: 587
I've asked for the app.PicoCom handler info at the next break when the whole trial should have been processed.
Maybe (According to dbstack) the callback never ends so can't be retriggered?
K>> dbstack
> In MouseOdor12/RunTrial (line 429)
In MouseOdor12/RerunIgnoredTrials (line 544)
In MouseOdor12/ManageResults (line 942)
In MouseOdor12/PicoInput (line 387)
In MouseOdor12>@(varargin)app.PicoInput(varargin{:}) (line 333)
In internal/Serialport/callbackFunction (line 1472)
In internal.Serialport>@(varargin)obj.callbackFunction(varargin{:}) (line 980)
I wondering if I have a problem re-entering a routine that's running already when I RerunIgnoredTrials though I've tried to avoid that. There is always an open arrow pointing to
configureCallback(app.PicoCom,"terminator",@app.PicoInput);
I guess meaning that it's supposed to be always running.
And as far as the suggestion above, I've already added to the input processor function
function PicoInput(app,src,~)
while (app.PicoCom.NumBytesAvailable>0)
do my stuff for one line
end
end
So that any responses missed and piling up will get handled until the buffer is cleared
Do I need to call PicoInput(...) on a timer or something to work around this problem? Remember there is NO main line code running all the time. Between actions the App Designer debugger is showing Run, not Continue until I hit a breakpoint. Should I add
if (app.PicoCom.NumBytesAvailable>0)
PicoInput(app,src,~)
end
into the RunTrial procedure to be sure it has finished?
This is not an easy one so please don't try to answer if you don't know App Designer applications using external callback functions intimately.
Gavin
Gavin 2024-9-15
I tried adding at the start of a RunTrial call
if (app.PicoCom.NumBytesAvailable>0)
PicoInput(app, app.PicoCom);
end
Seems like that should help. Tracing a bit when I try to enter a function Manage
ManageResults(app,app.TrialNumberField.Value, ...
app.PicoType,app.PicoData);
It pops up something ominous:
classdef (Hidden) AbstractNumericComponent < ...
matlab.ui.control.internal.model.ComponentModel & ...
matlab.ui.control.internal.model.mixin.EditableComponent & ...
matlab.ui.control.internal.model.mixin.HorizontallyAlignableComponent & ...
matlab.ui.control.internal.model.mixin.FontStyledComponent & ...
matlab.ui.control.internal.model.mixin.BackgroundColorableComponent & ...
matlab.ui.control.internal.model.mixin.PositionableComponent& ...
matlab.ui.control.internal.model.mixin.EnableableComponent & ...
matlab.ui.control.internal.model.mixin.VisibleComponent & ...
matlab.ui.control.internal.model.mixin.TooltipComponent & ...
matlab.ui.control.internal.model.mixin.PlaceholderComponent
% This undocumented class may be removed in a future release.
% This is the parent class for edit field components that only accept numeric inputs.
% e.g. Numeric edit field, Spinner
% Copyright 2014-2022 The MathWorks, Inc.
And NOT stepping into the ManageResults() function that needs to run!
---------------
New thought... My USB callback is just a function because in App Designer creating a callback using the button only allows callbacks to USER interaction. I noticed that AppD managed e.g. buttons with callbacks have CALLBACK EXECUTION CONTROL BusyAction options: queue or cancel
Can I set (or be sure that) my function that's a USB Serial callback will always be cued? Could this be the problem?

请先登录,再进行评论。

产品


版本

R2023b

Community Treasure Hunt

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

Start Hunting!

Translated by