BytesAvailableFcn is not triggered when serial data does not contain a terminator, but while loop works (even with serial options ,'BytesAva​ilableFcnM​ode','byte​','InputBu​fferSize',​1,'Termina​tor','')

조회 수: 15 (최근 30일)
I have a problem that the BytesAvailableFcn is not triggered when serial data does not contain a terminator. I set the serial options
  • 'BytesAvailableFcnMode','byte',
  • 'InputBufferSize',1,
  • 'Terminator',''
so that is should not require a terminator, and run the callback as soon as a single byte is available (please tell me if I missed something here...).
A while loop checking for BytesAvailable will properly get the data.
I tried different callback functions, including @instrcallback.
Please see the example below. I'm using an Arduino Uno to generate the serial data.
When using Serial.println on the arduino (e.g., the AnalogReadSerial official example sketch), we can use the default serial settings (option 1 in my program) and both options A (while loop) and B (callback) will work.
When using Serial.print on the arduino (e.g., the SerialCallResponse official example sketch), we should not wait for a terminator (option 2 in my program) and option A (while loop) still works, but option B (callback) won't.
Setting the BytesAvailableFcn in the serial(...) command or setting it before fopen does not make any difference.
Tested with MATLAB 2017b and 2018a on Windows10.
P.S. I know MATLAB has an add-on for arduino, but my problem is with data received over the serial port, not necessarily from an arduino.
% Try this with Arduino and
% * AnalogReadSerial (File->Examples->Basics) sketch
% Note: Arduino will send data terminated with a new-line character
% --> "default" settings work with both while loop and callback function
% * SerialCallResponse (File->Examples->Communication) sketch
% Note: Arduino will send "A" without new-line termination every 0.3s
% --> "single byte" reception works with while, but not with callback...
clear
port='COM4';
choice=input('Choose [1] for default (new-line terminated) or [2] for single byte (non-terminated) data reception.>');
switch(choice)
case 1
hCOM=serial(port,'BAUD',9600); % default settings with terminator
case 2
hCOM=serial(port,'BAUD',9600,'BytesAvailableFcnMode','byte','InputBufferSize',1,'Terminator','');
end
choice=input('Choose [A] for while loop or [B] for callback function.>','s');
fopen(hCOM);
switch(choice)
case 'A'
tic;
while(toc<5)
if hCOM.BytesAvailable
disp(fgets(hCOM)); % or % data=fgetl(hCOM); % or % data=fscanf(hCOM);
else
pause(.001); % wait a little (otherwise CPU will go to 100%)
end
end
case 'B'
hCOM.BytesAvailableFcn = 'disp(fgets(hCOM));'; % set the callback function.
% hCOM.BytesAvailableFcn = @instrcallback;
% hCOM.BytesAvailableFcn = @(src, event) disp(fscanf(src, '%s')); % set the callback function.
pause(5)
end
fclose(hCOM);
  댓글 수: 6
Daiichiro Matsunaga
Daiichiro Matsunaga 2018년 8월 20일
Very helpful Walter!
I feel that the documentation could be more clear on these points.
Daiichiro Matsunaga
Daiichiro Matsunaga 2018년 8월 20일
For anyone who is interested, here is a working example with comments. I tested it extensively, but please let me know if there are still any mistakes.
% Basic example showing how to read/convert data
% depending on how you send it on the Arduino side.
%
% Warnings:
% - COM port number is hard-coded, please check "port=..." below.
% - No check for port-already-open errors
% - No stop-timer to help prevent MATLAB from "forgetting" to stop if data
% comes in too quickly.
%
% Test with this example Arduino code.
%{
byte val;
void setup() {
Serial.begin(9600);
val = 0;
}
void loop() {
Serial.println(val); // --> use default serial settings with fscanf(hCOM,'%f')
//Serial.println('A');// --> use default serial settings with fgetl/fgets/fscanf(hCOM)
//Serial.print(val); // Should never be used, because it converts numbers to strings and then you don't know where one number ends and the following starts (does '123' mean [1,2,3] or [1,23] or [123] or ...?)
//Serial.print('A'); // --> use customized serial settings with char(fread(hCOM,1)')
//Serial.write(val); // --> use customized serial settings with fread(hCOM,1)
//Serial.write('A'); // same as print: --> use customized serial settings with char(fread(hCOM,1)')
val++;
delay(100);
}
% -----------------------------------------------
%}
%%Initialize
clear
port='COM4'; % specify your port here
hCOM=serial(port,'BAUD',9600); % default settings (with new-line terminator)
choice_ln=input('Choose [1] for new-line terminated data (Serial.println on Arduino), or [2] for non-terminated (Serial.print/Serial.write) data.>'); % Note that Serial.writeln doesn't exist
choice_txt_dat=input('Choose [1] for text strings or [2] for data values.>'); % we may need to convert strings to data or vice versa
choice_wh_cb=input('Choose [1] for while loop or [2] for callback function.>'); % just 2 different example implementations, depending on how you want to use the the incoming data in your code.
if(choice_ln~=1) % add customized settings to the serial object if no new-line characters are sent
set(hCOM,'Terminator',''); % there won't be a terminator character
if(choice_wh_cb==2) % using the callback function
set(hCOM,'BytesAvailableFcnMode','byte'); % don't wait for a terminater character, but use number of bytes received as trigger for the callback function
set(hCOM,'BytesAvailableFcnCount',1); % set how many bytes should be received before triggering the callback function
end
end
%%Open the COM port
fopen(hCOM);
switch(choice_wh_cb)
case 1
%%While loop
tic; % start a timer
while(toc<5) % while the timer is running for less than 5s
if hCOM.BytesAvailable
switch(choice_ln)
case 1
% println --> receiving a terminated string --> read using fgetl/fgets/fscanf
switch(choice_txt_dat)
case 1 % text
disp(fgetl(hCOM)); % result does not include new line character
% disp(fgets(hCOM)); % result includes new line character
% disp(fscanf(hCOM)); % result includes new line character
case 2 % data
% disp(str2double(fgetl(hCOM))); % read as string and convert to a numeric value. This will give NaN if actually receiving a string which is not a number
% disp(str2double(fgets(hCOM))); % read as string and convert to a numeric value. This will give NaN if actually receiving a string which is not a number
% disp(double(fgetl(hCOM))); % read as string and convert to a numeric value. This will convert a string to its ASCII value, e.g.: 'A'-->65 and '2'-->50
% disp(double(fgets(hCOM))); % read as string and convert to a numeric value. This will convert a string to its ASCII value, e.g.: 'A'-->65 and '2'-->50
disp(fscanf(hCOM,'%f')); % read as a floating point (numeric) value. This will give a warning and not convert if actually receiving a string which is not a number
end
case 2
% print or write --> receiving raw data values --> read using fread
switch(choice_txt_dat)
case 1 % text
disp(char(fread(hCOM,1)')); % read 1 byte, convert it to a character array, and display it (depending on the value, the character may not be displayable)
case 2 % data
disp(fread(hCOM,1)); % read 1 byte and display it (if it was sent as a string, it will show its ASCII value, e.g.: 'A'-->65 and '2'-->50)
end
end
else % no bytes available
pause(.001); % wait a little (otherwise CPU will go to 100%)
end
end
case 2
%%Callback function
hCOM.BytesAvailableFcn = @instrcallback; % just for checking if the function is actually called or not. If a valid choice was made, this will be overwritten.
switch(choice_ln)
case 1
% println --> receiving a terminated string --> read using fgetl/fgets/fscanf
switch(choice_txt_dat)
case 1 % text
hCOM.BytesAvailableFcn = @(src, event) disp(fgetl(src)); % result does not include new line character
% hCOM.BytesAvailableFcn = @(src, event) disp(fgets(src)); % result includes new line character
% hCOM.BytesAvailableFcn = @(src, event) disp(fscanf(src)); % result includes new line character
case 2 % data
% hCOM.BytesAvailableFcn = @(src, event) disp(str2double(fgetl(src))); % read as string and convert to a numeric value. This will give NaN if actually receiving a string which is not a number
% hCOM.BytesAvailableFcn = @(src, event) disp(str2double(fgets(src))); % read as string and convert to a numeric value. This will give NaN if actually receiving a string which is not a number
% hCOM.BytesAvailableFcn = @(src, event) disp(double(fgetl(src))); % read as string and convert to a numeric value. This will convert a string to its ASCII value, e.g.: 'A'-->65 and '2'-->50
% hCOM.BytesAvailableFcn = @(src, event) disp(double(fgets(src))); % read as string and convert to a numeric value. This will convert a string to its ASCII value, e.g.: 'A'-->65 and '2'-->50
hCOM.BytesAvailableFcn = @(src, event) disp(fscanf(src,'%f')); % read as a floating point (numeric) value. This will give a warning and not convert if actually receiving a string which is not a number
end
case 2
% print or write --> receiving raw data values --> read using fread
switch(choice_txt_dat)
case 1 % text
hCOM.BytesAvailableFcn = @(src, event) disp(char(fread(src,1)')); % read 1 byte, convert it to a character array, and display it (depending on the value, the character may not be displayable)
case 2 % data
hCOM.BytesAvailableFcn = @(src, event) disp(fread(src,1)); % read 1 byte and display it (if it was sent as a string, it will show its ASCII value, e.g.: 'A'-->65 and '2'-->50)
end
end
pause(5);
end
%%Close the COM port
fclose(hCOM);

댓글을 달려면 로그인하십시오.

답변 (0개)

카테고리

Help CenterFile Exchange에서 MATLAB Support Package for Arduino Hardware에 대해 자세히 알아보기

제품

Community Treasure Hunt

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

Start Hunting!

Translated by