Can't get variable out of Callback function. Already defined as global.

I have a serialport bytesavailable callback function.
function readFrame(src,~)
global data
global payloadBytes
message = read(src,payloadBytes,"uint8");
src.UserData = message;
data = message;
return
I read the buffer into the variable message and then save it as global variable data.
i get the variable in my workspace. Then something happens that I can't explain. I try to svae this global variable in a local variable in my Mainscript.
processedData = data;
Problem is processedData is always empty. So when i want to make a 2D Matrice to save different data liek this;
processedData(frameCount,:) = data;
I get an exception: indices doesn't match. Thats no wonder.
Can somebody tell me what I am doing wrong.
Thank you.

10 Comments

Could you confirm that in your main script you also declare data as global there?
Yes i forgot to mention it. I declared it as global variable in the main script.
If i debug the main script and point to the data variable with my mouse i see it has a value. Pointing to processedData variable shows me empty array...
Here is the Loop. First testing if data is available in data variable in a while loop
Then i save it in the messageByte Variable.
Empty data variable for next reading and isempty loop.
Count frames.
After 10 frames stop loop.
I send 10 frames with a periodicity of 500ms already tested with timer function and confirmed. But the timer function gave me errors. Sometimes it worked and sometimes it didn't worked. So i decided to switch to bytesavailable Callback.
Now i have this problem and i can't find the reason.
while(app)
while isempty(data)
disp("wait");
end
%Save frames
messageByte(frameCount,:) = data;
data = [];
%%
MagicOK = 0;
frameCount = frameCount +1
if frameCount == 11
app = 0;
end
end
processedData = data;
That is not going to update processedData except when the statement is executed. as data changes processedData is going to stay whatever size last assigned. When you start out the global variable data is going to be empty until you assign something else to it.
I suggest that initialize processedData as zeros(0,N) where N is the number of samples per reading that you expect.
The fscanf is going to grab all available samples until Terminator or timeout, which is not necessarily absolutely going to be the same number each time, so really you should be checking the size of data before you store.
Remember too that you might have multiple readFrame callbacks between places the main script processes the data. Including possibly none.
Are you familiar with the concept of ring buffers? I recommend them for any asynchronous data processing that needs to process each input exactly once.
Thank you very much. I will try out.
I thought, if data has a current value as it is global, i could save it to the another variable.
No i am not familiar with the concept of ring buffers, but i will take a look. It's exactly what i need. Procces each input exactly once.
if read offset is the same as write offset then the buffer is empty
otherwise pull the data out from read offset, and set read offset = mod(read offset, buffer size) + 1
similar logic for writing, write at write offset, mod+1 to get the new write offset.
So if i will do this, i will not use the callback function ?
I will implement it in my while-loop in the mainscript. i have a fixed payloadSize.
Because i tested the readFrame with an global value x = 1.
Then tried to write it in the main script. It does not work.
I think i have a lack of understanding. I can't work with data from the callback outside from the callback.
you would still have a call back function that stored the values in the circular buffer.
Okay i know have coded something i think it could work.
Maybe you can review it to make sure the concept ist right.
I made a circular buffer of size 16 with each cell a fixed size of 1xpayloadBytes.
Im not sure with the use of the counter as global variable.
function readFrame(src,~)
global payloadBytes
global cbuffw
global cbuff
message = read(src,payloadBytes,"uint8");
if cbuffw < 17
cbuffw=cbuffw+1;
elseif cbuffw == 17
cbuffw = 1;
end
cbuff(cbuffw,:) = message;
return
Function for Buffer
function [cbuff,cbuffr,cbuffw] = circularBuffer(size,payload)
%create a 1xpayload cell (preallocation).
cbuffSize = zeros(1,payload);
%create a ringbuffer with size x cbuffsize
cbuff(size,:) = cbuffSize;
%set writepointer at start
cbuffw = 1;
%set readpointer at start
cbuffr = 1;
end
Main script:
global cbuff
global cbuffw
[cbuff,cbuffr,cbuffw] = circularBuffer(16,payloadBytes);
Then in the main i would check if cbuffw matches cbuffw like you said.
Something like this:
if cbuffr == cbuffw
%no new data
elseif cbuffr < cbuffw
%read data out of cbuff
cbuffr=cbuffr+1
elseif cbuffr > cbuffw
%bufferoverflow throw error. Should not happen.
end
I think the last part is not correct.
Thank you very much.
Ok sorry maybe im too dumb to get it but it doesn't work. The callback works i get alle the data i need inside cbuff. But i can't check cbuffw inside my main skript and so cbuff doesn't contain the data. I feel really dumb.
In the main script.
cbuffw always 1.
cbuff always 1xpayloadBytes filled with zeros.
In the workspace
cbuff stores all values read.
cbuffw increases.
After stopping the mainscript i can see all the serialdata in cbuff.
Mainscript:
if cbuffr == cbuffw
disp("no new data");
elseif cbuffw > cbuffr
disp("w>r");
cbuffr = cbuffr+1;
data(frameCount,:) = cbuff(cbuffr,:) ;
frameCount= frameCount +1
if cbuffr == 16
cbuffr = 1;
end
end

Sign in to comment.

Answers (1)

Try assigning it like this
if isempty(data)
fprintf('Skipping frameCount = %d because data vector is empty!\n', frameCount);
elseif processedData <= 1 || isempty(processedData)
% The first time through just assign it so that processedData has the same number of columns as data
processedData = reshape(data, 1, []); % Make sure it's a row vector.
else
% Append on data into a new row for the second and subsequent times through.
processedData(frameCount,:) = data;
end

9 Comments

What line does it hit? And what are the sizes of processedData and data at that point?
data is always empty and so it only repeat the first if-statement..
Then you need to figure out why
message = read(src,payloadBytes,"uint8");
always returns null. We can't do that for you.
This line doesn't return null. I actually can see the data from serialport in my Workspace...
Then why did you say "data is always empty" when it's clearly not. It has 8384 values in it. Or it somehow became null before it go to the line where it's supposed to assign processedData. You need to find out why and where it went from 8384 elements to null.
i said processedData is always empty. Because data is always empty IN the Mainscript. But its not empty in my callback. Callback works. Saves the serial data in variable data.
Thats the problem that i don't understand and the problem why i asked this question.
Its scope is global in both the callback and the main script. So my issue is why "isempty(data)" never exits when it is clearly not empty.
Sorry for the confussion. I never had such a problem.
You need to get it visible in the same scope. For example attach it to the app structure, like
app.data = read(src,payloadBytes,"uint8");
Then app.data should be available everywhere because app is available everywhere. It's like a global variable.
I got the solution. I had to use drawnow(). With using drawnow() variable and its value was useable in the mainscript.

Sign in to comment.

Categories

Asked:

on 29 May 2022

Commented:

on 31 May 2022

Community Treasure Hunt

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

Start Hunting!