Reduce time execution in a real time serial data reading

29 views (last 30 days)
Hello everybody,
I'm carrying out an app that reads and plot data from sensors by serial Port. Data sent contains the values of 8 sensors plus power value and sample number. Size of the data can change because data of sensors values can changue as well. Data are sent every 0,5 s approximatly. I have to keep the data in a matrix (no problem), and plot in an axes.
If I just read values, time of each loop is fastier than the time that takes a new data to enter in the buffer, so I just have to wait for the new value, and then save it in a matrix. But If I try to plot the 8 values of the sensors, then the code takes longer than those half seconds, what makes that some data get missed. Which solution could I implement? I was thinking to save all the data but just plot one sensor in each iteration, so I'm plotting just one of each 8 values for each sensor. That's not important because after thousend of samples, visually it is impossible to see the difference, since the graph has the limit betwenn 0 and the n, where n can be +15000, but the importan thing which is save the data for processing after is performed.
But even with this last method, time of function is sometimes about 0,5 seconds and I get often an error in the matrix assignament because data collected from serial are no consistent. I was thinking play with the buffer because I could keep there more than one read, since the size is bigger than a packet of data.
The code without changes is the next one. Some ideas about how to get it? Thanks in advance.
function A = leerpuertoSerial(com,app)
hold(app.UIAxes,'on');
a = serial(com,'BaudRate',115200);
fopen(a);
A = zeros(10000,10);
n = 1;
i = 0;
cleanup = onCleanup(@()myCleanupFun(a));
ylim([0 800]);
while n<2000
if a.BytesAvailable > 0
i=i+1;
s =fscanf(a);
% I read the data packet which is something like this 1,20,343,234,234,234,543,543,543,343
C = textscan(s, '%d%d%d%d%d%d%d%d%d%d','delimiter',',');
A(i,:) = cell2mat(C);
%I plot just from the 3 value up to 10 th value (1 to 8 th sensor)
plot(app.UIAxes,i,A(i,3),'x-b');
plot(app.UIAxes,i,A(i,4),'x-b');
plot(app.UIAxes,i,A(i,5),'x-g');
plot(app.UIAxes,i,A(i,6),'x-k');
plot(app.UIAxes,i,A(i,7),'x-m');
plot(app.UIAxes,i,A(i,8),'x-c');
plot(app.UIAxes,i,A(i,9),'x-b');
plot(app.UIAxes,i,A(i,10),'x-r');
%Legends makes really slow the code.
%legend(app.UIAxes,'s1','s2','s3','s5','s6','s7','s8');
drawnow;
end
n = n+1;
flushinput(a);
end
hold(app.UIAxes,'off');
msgbox('Proceso Terminado');
xlswrite('OUTPUT',A);
end
function myCleanupFun(a)
fclose(a);
end

Answers (1)

J. Alex Lee
J. Alex Lee on 27 Feb 2020
If instead of a loop, you use a timer (https://www.mathworks.com/help/matlab/matlab_prog/use-a-matlab-timer-object.html), you might have enough control to do what you want.
I think in general the plot is slow...if you don't need each plot to have a distinct symbol, maybe you can combine the plot into a single command. You might also save time by plotting once, and updating the XData and YData.
Another problem is that A seems to grow on every iteration, and this can slow down code especially if A is really large.
Isn't it redundant to use fscanf together with textscan? I don't know if it will affect execution time significantly, but how about fileread() instead of fscanf?
I think a better overall approach would be to decouple your data collector and displayer.
  4 Comments
J. Alex Lee
J. Alex Lee on 28 Feb 2020
Triggering your save+display routine with the event supplied by the serial() object seems like a good idea, but it's not clear to me what you're asking with this new scheme.
I would still think you want to trigger only data saving when data is available. You can have that data saving function trigger another event to start the display function (which hopefully my above comment can help with), and apply some interruption logic to that so you can skip displays or whatever.
If you anticipate really going into the tens of thousands of data points, you might consider resampling the data for the display to ensure that you never have more than some number of data points displayed in the screen.
Carlos Molina
Carlos Molina on 6 Mar 2020
Thanks a lot for your answer, that was really helpfull.
I did some modification, I'm using now a animated line rather to plot and I update the X and Y data. Time of operation is quite good, about 0.01-0,04 seconds.
From the device I send the data as ASCI, but I will change after to send as a binari to save bytes and add check sum, etc. The device read the 8 sensors chanels more the power and send a colection of ASCI characters with NºSampling,Power,S1,....S8. After 8 packets of data I send an a LR terminator. In the matlab function, I read data when the LR character is received. Then I save all the data and I display just one of these 8 data packet, which is quite enough. Each data packet is sent from the device every 450 ms, so I have 8x450ms to perform everything in matlab.
Also, I make a security copy in the excel file where I want to store the data for further processing, so in case a crash happends, data lost from the last security copy is insignificant.
Well, rather to use the axes of the app, I use the tradicional figure, I prefer the format. Still I have some problems, for example, first reading get always damage, is not important because first data packet does not contains any important data, but I will figure out. Another issue is that sometime, after several thousend of data ocurrs a corruption in the data read or the data get lost. This could be just an error in the serial data communication, I will try to improve the serial protocol. Time of perform the reading keeps stable along the time so it does not relevant for the error, I think.
For the animated line, I set as maximum points 200 M points, which is more than I expect.
function Leersensoractivo(com,fullfileName)
%Delete residual port
delete(instrfindall);
%Define serial port.
s = serial(com,'BaudRate',115200);
%Set size of buffer
s.InputBufferSize = 1024;
if (s.Status == 'closed')
%fopenport is a own function as fopen but returns 0 or 1 if port can be open.
st = fopenport(s);
if st == 0
errordlg('Port COM not available','Port COM error')
else
fopenport(s);
figure('Name','ENOSE-DATA','NumberTitle','off');
fig = gcf;
grid on;
%Creat an animated line for the 8 sensor and power.
h1 = animatedline;
h2 = animatedline;
h3 = animatedline;
h4 = animatedline;
h5 = animatedline;
h6 = animatedline;
h7 = animatedline;
h8 = animatedline;
pw = animatedline;
set(h1,'Color','b','MaximumNumPoints',200000000,'DisplayName','s1');
set(h2,'Color','r','MaximumNumPoints',200000000,'DisplayName','s2');
set(h3,'Color','g','MaximumNumPoints',200000000,'DisplayName','s3');
set(h4,'Color','m','MaximumNumPoints',200000000,'DisplayName','s4');
set(h5,'Color',[0.8500 0.5000 0.1000],'MaximumNumPoints',200000000,'DisplayName','s5');
set(h6,'Color',[0.8500 0.3250 0.0980],'MaximumNumPoints',200000000,'DisplayName','s6');
set(h7,'Color',[0.4940 0.1840 0.5560],'MaximumNumPoints',200000000,'DisplayName','s7');
set(h8,'Color',[0.4660 0.6740 0.1880],'MaximumNumPoints',200000000,'DisplayName','s8');
set(pw,'Color','k','MaximumNumPoints',200000000,'DisplayName','Power','LineWidth',0.8);
global A;
A = zeros(1,11);
%Since the first xlswrite execution is the slowest, I perform out before start to read serial port.
xlswrite(fullfileName,A);
A = zeros(200,11);
global i;
global contador;
global numitr;
numitr = 1;
contador = 0;
i=1;
s.BytesAvailableFcn = {@Call,fig,h1,h2,h3,h4,h5,h6,h7,h8,pw,fullfileName} ;
end
end
end
function Call(obj,event,fig,h1,h2,h3,h4,h5,h6,h7,h8,pw,fullfileName)
tic;
global contador;
global numitr;
global i;
global A;
M = zeros(1,10);
if ishghandle(fig)==1
cadena = fscanf(obj);
N = textscan(cadena, '%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d','delimiter',',');
C = cell2mat(N);
for k=1:10
M(k) = double(C(k));%I take the firs data packet of the 8 to display.
end
b = numitr;
addpoints(pw,b,M(2));
addpoints(h1,b,M(3));
addpoints(h2,b,M(4));
addpoints(h3,b,M(5));
addpoints(h4,b,M(6));
addpoints(h5,b,M(7));
addpoints(h6,b,M(8));
addpoints(h7,b,M(9));
addpoints(h8,b,M(10));
n = i;
%Save data.
A(n,1:10) = C(1:10);
n=n+1;
A(n,1:10) = C(11:20);
n=n+1;
A(n,1:10) = C(21:30);
n=n+1;
A(n,1:10) = C(31:40);
n = n+1;
A(n,1:10) = C(41:50);
n = n+1;
A(n,1:10) = C(51:60);
n = n+1;
A(n,1:10) = C(61:70);
n = n+1;
A(n,1:10) = C(71:80);
numitr = numitr + 8;
axis([0,numitr,0,1000]);
drawnow update;
n = n+1;
i = n;
if i == 47 %Here I carry out the secury copy. and reset I, so I'm doing a
%security copy every 48 readings.
p = contador*(i-1)+1;
xlswrite(fullfileName,A(1:i-1,:),1,sprintf('A%d',p));
contador = contador +1;
clear A;
global A;
A = zeros(200,11);
i=1;
end
A(n-1,11) = toc;%I store the tic-toc time to control time of execution.
else
%If user close the figure, I store the remained data from the last security copy
B = A(1:i-1,:);
a = numitr;
p = a-i+1;
xlswrite(fullfileName,B,1,sprintf('A%d',p));
msgbox('xls file created');
fclose(obj);
end
end

Sign in to comment.

Products


Release

R2018b

Community Treasure Hunt

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

Start Hunting!