Receive packets

Hi,
I have a question about the fullduplexserial object.
I want to receive some packets and want to save one in memory.
A packet always starts with $FB and ends with $04. (packet lenght is between 8 and 16 bytes)
I can receive a packet, but have trouble to hold one in memory.
Can somebody help me further with this?
this is what i have so far...
I have a question about the fullduplexserial object.
I want to receive some packets and want to save one in memory.
A packet always starts with $FB and ends with $04. (packet lenght is between 8 and 16 bytes)
I can receive a packet, but have trouble to hold one in memory.
Can somebody help me further with this?
this is what i have so far...
CON _clkmode = XTAL1|PLL16X
_xinfreq = 5_000_000
OBJ
com: "FullDuplexSerial"
serial: "Parallax Serial Terminal"
VAR
byte char
byte packet[16]
byte packetStart
PUB Main | ptr
'com.start(rxpin, txpin, mode, baudrate)
com.start(0,1, 00, 38400)
serial.start(115200)
repeat
com.RxFlush
char:=com.rx
if char == -1
ptr:=0
if char == 15 'packet start
packet[ptr]:=char
ptr++
if char == 4 'packet end
packet[ptr]:=char
serial.dec(ptr)
serial.char(10)
serial.char(13)
ptr:=0
Comments
You said the packet starts with $FB, but your code indicates that it starts with 15. In any event, try this for your main loop. (I'm assuming that $FB is correct.) It will store everything between $FB and $04 to your packet array.
repeat repeat until com.rx == $FB ptr~ repeat until (char := com.rx) == $04 packet[ptr++] := char
-Phil
(If you indentation is really like it's displayed here in the thread)
repeat com.RxFlush ' empty any transmissions that might have been missed so far - OK char:=com.rx ' read one character - OK if char == -1 ' com.rx will not return -1 as far as I know. -1 is returned by rxTime when the time passed by without receiving a character ptr:=0 ' so, here you set prt to 0 in a piece of code that never runs if char == 15 'packet start ' this will only happen once the transmission start code is send. packet[ptr]:=char ' so this code will only be executed once as well. In the end you only store the start-byte and the end byte and nothing else ;o) ptr++ if char == 4 'packet end ' find the end - OK packet[ptr]:=char ' but as start and end are always the same there is no added value in storing these bytes serial.dec(ptr) serial.char(10) serial.char(13) ptr:=0.
And here a suggestion for an enhancement of Phils code. It can happen that you have transmission problems ... or maybe do a mistake in setup of the senders baud-rate ... whatever.
If you have such kind of problems it can happen that you don't see the END-character after 16 bytes. With Phils code you'd continue to read and store and you'd overwrite important memory locations sooner or later.
It's only a small amount of additional memory needed for a small check, but it saves your program in case of these transmission problems.
BTW with phils code a buffer size of 14 is enough, as the start- and end-characters are not stored
repeat repeat until com.rx == $FB ptr~ repeat until ((char := com.rx) == $04) or (ptr>13) packet[ptr++] := char if char==$04 ' do whatever you want to do with the package .. call functions .. whatever ' else the package is ignored because the end sign has not been received in time
It's also a good practice to use constants. One reason is for making the code better readable and second reason is to allow easy changes without searching all the places that have to be changed. For example if you want to increase the size of the buffer. So, here is my final proposal:
CON START_OF_PACKET = $FB END_OF_PACKET = $04 BUFFER_SIZE = 14 VAR byte char byte packet[ BUFFER_SIZE ] PUB main ... repeat ' skip all characters until the start-char has been received repeat until com.rx == START_OF_PACKET ptr~ ' read characters until the end-character has been received or the buffer would overflow repeat until ((char := com.rx) == END_OF_PACKET) or (ptr>constant(BUFFER_SIZE-1)) packet[ptr++] := char ' recheck the end-condition - in case the last read character is a end-char everything is fine and ' the package can be processed if char==END_OF_PACKET ' do whatever you want to do with the package .. call functions .. whatever ' else the package is ignored because the end sign has not been received in time
So, the START/END_OF_PAKET constants are mainly for better readability. But it might also help later on in copy and paste code development. If you are in a new project and realize that this piece of code would also work there but with different characters you only have to change the constants.
Constants like BUFFER_SIZE are always usefull. It avoids bugs due to typos and in bigger code it's easier to resize the buffer because no code-change is needed. For example you might find out that it's better to read 2 packets before processing.
var byte packet[32] pub main | t, state, c, idx term.start(RX1, TX1, %0000, 115_200) pause(1) state := 0 idx := 0 t := cnt repeat c := rx.check case state 0: if (c == $FB) packet[idx++] := c state := 1 1: if (c => 0) packet[idx++] := c if (c == $04) ' process packet data ' idx is legnth of packet ' other loop code here waitcnt(t += (25 * MS_001))
In the event your code doesn't want to sit around waiting on a packet, that is, it could be doing other things, this structure may be helpful (combined with the other great tips in this thread).
Just solved a little problem here with the package length.
Sometimes i received a $04 char as the third byte (package length)
This is what i have at the moment.
PUB Main | ptr,i ' set pins 3 to 10 to outputs dira[3..10]~~ 'com.start(rxpin, txpin, mode, baudrate) com.start(0,1, %0000, 38400) serial.start(115200) ' skip all characters until the start-char has been received ' repeat until com.rx == START_OF_PACKET repeat com.RxFlush repeat until com.rx == START_OF_PACKET ptr~ packet[ptr++] := com.rx 'get priority packet[ptr++] := com.rx 'get address char := com.rx 'get packetlength repeat i from 0 to char-1 packet[ptr++] := com.rx 'get byte packet[ptr++] := com.rx 'get crc repeat until com.rx == END_OF_PACKET 'serial.dec(ptr) 'repeat i from 0 to ptr-1 ' serial.hex(packet[i],2) ' serial.str(string(",")) ' serial.char(13) ' '' we have received a full package ' let start parse it if packet[0]==32 ' we received a packet from module address 20 if packet[2]==1 'output pin 3 high serial.str(string("Output 3 High")) !outa[3]
Now the package sender needs some data back. But first i have to look how i can do it.
For those who are interested
I'm experimenting with some velbus modules: http://www.velbus.eu/products/view/?id=387354
First thing: what is the difference in those 2 pieces of code (with really no additional code inside of the repeat!) ??
com.RxFlush
repeat until com.rx==START_OF_PACKET
Flush will simply throw away everything in the full duplex internal buffer. The repeat-loop on the other hand will only throw away something if there is unexpected garbage.
Let's say after receiving a valid package you will do some more time consuming processing. The full duplex still buffers bytes coming from your device even if you are busy. So, most propable it will send START_OF_PACKET followed by the first bytes of the next package. When you're done with processing, why would you want to throw away this valid data?
Second thing: why would you add all this extra code?
ptr~ packet[ptr++] := com.rx 'get priority packet[ptr++] := com.rx 'get address char := com.rx 'get packetlength repeat i from 0 to char-1 packet[ptr++] := com.rx 'get byte packet[ptr++] := com.rx 'get crc repeat until com.rx == END_OF_PACKET
Reading from START_OF_PACKET til END_OF_PACKET will perfectly do the job! You can inspect all the bytes after receiving! AND it is safer! What happens if you simply have a bit-read-error in the packetlength when receiving the most significant bit? This would make your inner repeat run 128+x times instead of x times.
My proposal:
CON START_OF_PACKET = $FB END_OF_PACKET = $04 BUFFER_SIZE = 14 [color=green] ' define the index of different data inside of the packet POS_PRIORITY = 0 POS_MODULE_ADDRESS = 1 POS_DATA_SIZE = 2 OFFSET = 4 ' real packet size is packetlength as received ' plus the extra bytes received for priority, address, packetlength and crc OFFSET_CRC = 3 PIN_LED = 3 [/color] VAR byte char[color=green], crc[/color] byte packet[ BUFFER_SIZE ] PUB main ... repeat ' skip all characters until the start-char has been received repeat until com.rx == START_OF_PACKET ptr~ ' read characters until the end-character has been received or the buffer would overflow repeat until ((char := com.rx) == END_OF_PACKET) or [color=red](ptr>constant(BUFFER_SIZE-1))[/color] packet[ptr++] := char [color=green]' add the CRC-code here[/color] ' recheck the end-condition - in case the last read character is a end-char a full packet has been received ' now check the important content: packetsize (as announced in the packet) + OFFSET should match with ptr ' and the CRC send has to match with the calculated CRC if (char==END_OF_PACKET) [color=green]and ((packet[ POS_DATA_SIZE ]+OFFSET)==ptr) and crc==packet[ packet[ POS_DATA_SIZE ]+OFFSET_CRC ][/color] ' do whatever you want to do with the package .. call functions .. whatever [color=green]if packet[ POS_MODULE_ADDRESS ]==32 if packet[ POS_DATA ]==1 !outa[ PIN_LED ] [/color] ' else the package is ignored because the end sign has not been received in time
The code in red saves your *** if you have transmission-problems. For example you switch on the light exactly when the most significant bit of the packetlength is transferred. I know these things propably won't happen for days, weeks, month or longer, but when these things happen it even can destroy your device.
Let's imagine your code would then overwrite a variable or some piece of code which sets the DIRA-register and by accident some pins attached to output pins of pheripherals will be set to output on the propeller as well. *BAZINGA* ;o)
I don't know the specs of your packages, so I did not add the CRC-code. This is the only thing I would add to the original receiving loop because then you can immediately decide if the package is OK after receiving the END_OF_PACKET. But you need to know which bytes are included in the CRC. Is it only the data or is it data and header (priority, address and packetlength)? What CRC-function is used? Simple XOR?
PS: LOL .... saves your *** ... are you so prude on the other side of the ocean? Then you need to check this as well: ass
Got this from the velleman forum about the crc:
Your code: repeat until ((char := com.rx) == END_OF_PACKET) or
What if the second byte is the 'end_of_packet'? it simply stopsI don't know if you noticed, i'm from Belgium
Loop one (the outer one) is looping forever and repeats the whole process over and over again. Initialize buffer, ptr and crc variable Loop 2 is waiting for the START_OF_PACKET and skipping all other characters Loop 3 is filling the buffer until a) END_OF_PACKET has been received OR b) the buffer is full b) should usually only happen if there is a transmission-error when receiving the END_OF_PACKET. Otherwise the BUFFER_SIZE constant is to small. If the packet is OK then process it, otherwise the outer loop starts again
So, what happens if the second byte is END_OF_PACKET?
Of course the 3rd loop will immediately end, leaving prt set to 0. Now have a look at the following part of the if statement:
[b][color=blue]packet[ POS_DATA_SIZE ]+OFFSET)==ptr[/color][/b]
which evaluates to ?+OFFSET == 0, which is false in any case because OFFSET alone is > 0.? reminds me that the buffer should be initialized before getting filled with data again. So the following lines should be added/changed:
PUB main ... repeat [color=green] ' initialize for the next package bytefill( @packet, 0, BUFFER_SIZE ) crc~ ' or maybe crc := START_OF_PACKET, depending on whether tha character also counts ptr~ [/color] ' skip all characters until the start-char has been received repeat until com.rx == START_OF_PACKET [s][color=green]ptr~[/color][/s] ' read characters until the end-character has been received or the buffer would overflow repeat until ((char := com.rx) == END_OF_PACKET) or (ptr>constant(BUFFER_SIZE-1)) packet[ptr++] := char ' add the CRC-code here [color=green]crc := (crc-char+1) & $FF[/color] ' remove the crc byte send from the calculated crc - this is faster than having an if-statement in the crc-calculation crc := (crc + packet[ packet[ POS_DATA_SIZE ]+OFFSET_CRC ] - 1) & $FF ' recheck the end-condition - in case the last read character is a end-char a full packet has been received ...
Hopefully I did not add to many bugs, as I can't test at the moment.
What do you mean with that? Do loops work different in Belgium? ;o)
PS:
@JonnyMac:
Thanks for sharing this! I'm not sure if it's needed in Tumbler's project, that's why I did not add it to my proposed code so far.
I'd propably not put the waitcnt(t += (25 * MS_001)) into the code addressed to a beginner without further comments. Guess it means that the loop will be executed each 25ms. But 25 ms makes 360 bytes that can be received in the meantime with the current baudrate. So it very much depends on the device attached if this code workes over the long run. Having a device continuously sending at that speed means that the buffer of the full duplex serial will overflow very soon. Am I right?
http://www.velleman.eu/downloads/0/velbus/manuals/protocol/protocol_vmb8pbu.pdf
Funny thing is that it's really easy for me to understand what's written on the page in your language.
What puzzles me a bit is that this document says the START_OF_FRAME is always zero and the END_OF_FRAME is $7F, it lists some more bytes to be send before the data length code.
All in all this is a bad documentation! It does not say a single word about the baudrate and how many frames are send per second ..... or whether the inter-frame byte is continuously send ...
Good documentation always comes with examples.
So, without having such a device you can't get ready to run code from the forum. You have to find out things by yourself.
PS: The CRC code in my post has to be changed, because the CRC is 16 bit - I thought we were talking about 8 bit. Can you do that by yourself? The idea stays the same.
PPS: WAAAAAAA .... bad documentation ... or is the CRC 15 bit because the document says CRC15...CRC1? Typo? Who knows?!
Good luck ;o)
I have a lot of modules here, nice thing is that i have a log function in the software (velbus link)
I can see what packages are send and received between the modules.
I'm sure, the crc is only 8 bit.
baud is 38400, and i thougt 60 or 100 mSecs between a package.
I will take a look at your code later, thx for the help
Grtz Luc