Parsing data from serial port

Hi,
I communicate with a DsPIC
i have a constant format for a message:
HEADER1 HEADER2 OPCODE DATA CHECKSUM (no spaces)
in my MATLAB GUI i want to read the incoming message, chk if i get the HEADER1 I EXPCT, then chk to see i get HEADER2 i expect , if so: get OPCODE and DATA and do with that something
how can i split my message?
thought about switch;case
wanted to know if there is something more "elegant"
Thanks

2 Comments

Is that a text format or a numeric format? are the headers variable length? How can you tell when HEADER1 stops and HEADER2 begins?
headers ARE FIXED
header1: 55
header2: AA
if i get a sequence of 55AA i know now comes the opcode then the data
if i get 55555555555 i wait for AA because HEADER1 was recieved
if i get 55AF i wait to get 55 again because sequence 55AA was not recieved

Sign in to comment.

 Accepted Answer

You need a Finite State Machine. The Mathworks tools for that are http://www.mathworks.com/discovery/finite-state-machine.html but you probably do not need anywhere near the full power of that.
current_state = 0;
while true
this_input = fread(s, 1, 'uint8');
if feof(s); break; end
if state == 0 && this_input == 88 %0x55
current_state = 1;
elseif current_state == 1 && this_input == 170 %0xAA
current_state = 2;
now read the opcode and data and so on.
When you are through with it
current_state = 0;
else
current_state = 0;
end
end

6 Comments

Daniel
Daniel on 12 Apr 2016
Edited: Daniel on 12 Apr 2016
ok, so here is what i tried: i have a button ('READ') that is intended to check if any message exist in the buffer. analyze the message i.e. check if the message is valid (HEADERs) GET THE OPCODE, and according to opcode - write to 3 different boxes. example:
55 AA 02 1B CHKSUM
H1 H2 OP DATA CHKSUM
OPCODE 02 : write to box 'temperature' 27(0X1B)
OPCDODE 03: write to box 'voltage' the DATA
BUT NOTHING APPEARS IN THE BOX... here is my actual code for the button 'READ' press:
%%--- Executes on button press in rxButton.
function rxButton_Callback(hObject, eventdata, handles)
current_state = 0;
while (handles.SerPIC.BytesAvailable >= 5)
this_input = fread(handles.SerPIC,1, 'uint8');
% if feof(SerPIC);
% break;
% end
switch current_state
case 0 % state 0 wait for HEADER_1
if (this_input == 85)
current_state = 1;
else
current_state = 0;
end
case 1 % state 1 wait for HEADER_2
if (this_input == 170) %was 170
current_state = 2;
recieved_checksum = 255;
elseif (this_input == 85)
current_state = 1;
else
current_state = 0;
end
case 2 % state 2 wait for OPCODE
OPCODE = this_input;
current_state = 3;
recieved_checksum = mod(recieved_checksum + this_input,255);
case 3 % state 3 wait for DATA
DATA = this_input;
current_state = 4;
recieved_checksum = mod(recieved_checksum + this_input,256);
case 4 % state 4 wait for CHECKSUM
current_state = 0;
recieved_checksum = recieved_checksum - this_input;
if (recieved_checksum == 0)
% data is valid and can be read
if (OPCODE == 2)
set(handles.temp_read,'string',(DATA))
elseif (OPCODE == 3)
set(handles.voltage_read,'string',DATA)
elseif (OPCODE == 4)
set(handles.current_read,'string',DATA)
end
end
end
end
guidata(hObject,handles)
end
case 2, you use 255 as your mod, case 3 your use 256 as your mod.
case 2, you are starting with receive_checksum of 255 of type double. this_input is type uint8 . double + uint8 is evaluated by converting the uint8 to double, doing the addition, and converting the result to uint8, including "saturating" at 255 . uint8(255 + 27) is 255, take that mod 255 to get uint8(0) . Or uint8(255) if the mod is changed to 256. This is probably not what you wanted.
Similar saturation problem with case 3.
Case 4... remember that subtraction of uint8 underflows to 0, so you are going to have more 0's than you want. If you want to test equality, test equality.
Now around all of this is that you probably should not be using while() on there being at least 5 bytes available. Not all the bytes might have arrived yet, and your code does not wait for 5, it quits if there are not 5 or more available. Just read the bytes as needed. You can watch out for timeouts if you are worried about data going missing.
thanks!! corrected the mod256 got rid of the uint8 changed the while to while >=1 now its working. thanks a lot!!! still not sure about the while though... concerned it might stuck my problem if there is a situation where the buffer always has something stored...
Hi (again), Ok so Sending data is done by initiating a button press. My issue now is with recieving data. At the moment i have a state machine (see above)that runs with:
While(bytes.avaliable>=1)
But what i really want is an interrupt.
I can recieve at any moment data. I need the interrupt to jump to the state machine and remember at which state i am now. I have no lead on how to do so, especially since i havent figured out how to "remember" parameters between callbackd
"persistent". Or a shared variable if you need to be able to reset the state.
I'll try here: so i set in the "connect" button the following:
handles.SerPIC.BytesAvailableFcn = {'@intcon1',handles};
set(handles.SerPIC,'BytesAvailableFcnMode','byte');
set(handles.SerPIC,'BytesAvailableFcnCount', 1);
and the call back function is:
if true
function intcon1(hObject, eventdata, handles)
persistent current_state
persistent L;
persistent i;
persistent DATA;
persistent recieved_checksum;
% current_state = 0;
% while (handles.SerPIC.BytesAvailable >= 1)
this_input = fread(handles.SerPIC,1);
switch current_state
case 0 % state 0 wait for HEADER_1
L=0;
i=0;
DATA=0;
recieved_checksum=0;
if (this_input == 85)
current_state = 1;
else
current_state = 0;
end
case 1 % state 1 wait for HEADER_2
if (this_input == 51) %was 170
current_state = 2;
recieved_checksum = 136;
elseif (this_input == 85)
current_state = 1;
else
current_state = 0;
end
case 2 % state 2 wait for message length
MESSAGE_LENGTH = this_input;
L=MESSAGE_LENGTH;
i=1;
current_state = 3;
recieved_checksum = mod(recieved_checksum + this_input,255);
case 3
% if (s.BytesAvailable == MESSAGE_LENGTH)
DATA(i) = this_input;
recieved_checksum = mod(recieved_checksum + this_input,255);
i=i+1;
L = L - 1;
if L > 0
current_state = 3;
else
current_state = 4;
end
case 4
recieved_checksum = recieved_checksum - this_input;
if (recieved_checksum ==0)
set(handles.temp_read,'string',[num2str(DATA(1))])
set(handles.voltage_read,'string',[num2str(DATA(2))])
set(handles.current_read,'string',[num2str(DATA(3))])
current_state = 0;
else
current_state = 0;
end
end
guidata(hObject,handles)
end
but nothing seems to happen. and i get an error:
if true
Error using feval
Invalid function name '@intcon1'.
Error in instrcb (line 36)
feval(val{1}, obj, eventStruct, val{2:end});
Warning: The BytesAvailableFcn is being disabled. To enable the callback property
either connect to the hardware with FOPEN or set the BytesAvailableFcn property.
end

Sign in to comment.

More Answers (0)

Categories

Asked:

on 11 Apr 2016

Commented:

on 19 Apr 2016

Community Treasure Hunt

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

Start Hunting!