So now is that string representation, say "1234", terminated with a NULL byte (like in C) or does it have a byte at the beginning indicating the length (like in Pascal)?
Either way the length is built into the string and you need only pass it's address to the next function. The next function may then need to deal with the length issue.
I want to pass some data to FullDuplexSerial as a decimal value, however I need to know the size of that value to know when to stop reading. For example, when I reach, a delimiter, the next value will be data size, which will indicate how many bytes to read to get the actual value I seek.
Here is a better example of what I am trying to accomplish. US is the delimiter.
Sender.Tx(STX)
{ Format and send main data content between here and the next comment }
Sender.Dec(DataSize)
Sender.Tx(US)
Sender.Str(DataString)
Sender.Tx(US)
Sender.Dec(CRCSize)
Sender.Tx(US)
Sender.Dec(CRC)
{ Main data content should have been formatted and sent by this point }
'Send End Of Text message to the Receiver
Sender.Tx(ETX)
It's still not clear what you're asking. If you're returning the number as an ASCII string, the way to specify the length of the string is to use a zero byte at the end of the string value and to return the address of the start of the string. As mentioned, Pascal and some other languages store the length of the string in the first byte of the string and the actual string characters begin at the 2nd byte. The address of the length byte is what is returned. If you want to return two items from your function, one a small number (a length or size) and one an address (pointer), you can make use of the fact that, on the Propeller I, addresses are always 16 bits or less, so you can return the small number in the upper byte of the 32 bit return value and the address (pointer) in the lower 3 bytes. You'd use something like: "RETURN (smallNumber << 24) | (address & $FFFFFF)" in the function and you'd need to do something like this in the routine calling the function:
So you are saying an example string might look like:
;3;593;
Where the delimiter is the semi-colon, the length is 3 and the data is 593?
In which case if you have the address of all that as "strAddr" you can use BYTE[strAddr] which will get the first semi-colon. Then increment strAddr and use BYTE[...] again which will get you the length of 3. And so on.
So just put that in a loop and check for the delimeters and values as you go around.
When you get to the value part just pass it to FullDuplexSerial one character at a time as they are discovered. "ser.tx(someChar)"
However, if you have all these nice delimeters in your strings why bother putting the length in there anyway?
Because I am using a cyclic redundancy check to ascertain the validity of the data.
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
#1, HOME, GOTOXY, #8, BKSP, TAB, LF, CLREOL, CLRDN, CR
#14, GOTOX, GOTOY, CLS
BUFFER_SIZE = 80
'Transmission and reception constants
STX = 2 'Start Of Text
ETX = 3 'End Of Text
ENQ = 5 'Enquire the RESULT of the transmission
ACK = 6 'Positive RESULT of a successful transmission
NAK = 21 'Negative RESULT of an unsuccessful transmission
US = 31 'Data block seperator which is used for parsing
'Cyclic redundancy check constants
CRC_HEAD = %1110_0000 '4 bit tag, 4 bit payload
CRC_MASK4 = %0000_1111 'Payload mask
CRC_TAIL = %10_000000 '2 bit tag, 6 bit payload
CRC_MASK6 = %00_111111 'Payload mask
VAR
LONG stack[32]
BYTE Buffer[BUFFER_SIZE]
OBJ
Terminal : "FullDuplexSerial"
Sender : "FullDuplexSerial"
Receiver : "FullDuplexSerial"
PUB Main
Terminal.Start(31, 30, 0, 115_200)
Receiver.Start(0, 1, 0, 9600)
WAITCNT(CLKFREQ * 2 + CNT)
Terminal.Tx(CLS)
Terminal.Rxflush
COGNEW(SendDataStringWithCRC2, @stack)
MonitorForIncomingData
PUB MonitorForIncomingData | Index, Char
'Monitor for Start Of Text (Incoming Data)
REPEAT UNTIL Receiver.Rx == STX
'Start Of Text has been found
'Start buffer filling process
Terminal.Str(string("Buffer filling has started!", CR))
'Clear the buffer and fill it with zeros
BYTEFILL(@Buffer, 0, BUFFER_SIZE)
'Set buffer array index to zero
Index := 0
'Fill the buffer with pertinent data until
'End Of Text is found
REPEAT WHILE (Char := Receiver.Rx) <> ETX
Buffer[Index] := Char
Index++
'Buffer filling complete
Terminal.Str(string("Buffer filling has ended!", CR))
'Display the data contained within the Buffer
Terminal.Str(@Buffer)
Terminal.Tx(13)
PUB SendDataStringWithCRC1(DataString) | DataSize, CRC
Sender.Start(1, 0, 0, 9600)
WAITCNT(CLKFREQ * 4 + CNT)
DataSize := STRSIZE(DataString)
CRC := ComputeCRC(@DataString, DataSize)
'Send Start Of Text message to the Receiver
Sender.Tx(STX)
{ Format and send main data content between here and the next comment }
Sender.Dec(DataSize)
Sender.Tx(US)
Sender.Str(DataString)
Sender.Tx(US)
Sender.Dec(CRC)
{ Main data content should have been formatted and sent by this point }
'Send End Of Text message to the Receiver
Sender.Tx(ETX)
PUB SendDataStringWithCRC2 | DataString, DataSize, CRC
Sender.Start(1, 0, 0, 9600)
WAITCNT(CLKFREQ * 4 + CNT)
DataString := STRING("The big, bad, burly, brown bear, barely bit the beautifully bestowed bosoomly blonde.")
DataSize := STRSIZE(DataString)
CRC := ComputeCRC(@DataString, DataSize)
'Send Start Of Text message to the Receiver
Sender.Tx(STX)
{ Format and send main data content between here and the next comment }
Sender.Dec(DataSize)
Sender.Tx(US)
Sender.Str(DataString)
' Sender.Tx(US)
' Sender.Dec(CRCSize)
Sender.Tx(US)
Sender.Dec(CRC)
{ Main data content should have been formatted and sent by this point }
'Send End Of Text message to the Receiver
Sender.Tx(ETX)
PUB ComputeCRC(DataPointer, SizeOfBytes)
REPEAT SizeOfBytes
RESULT ^= (BYTE[DataPointer++] << 8)
REPEAT 8
RESULT <<= 1
IF RESULT & $10000
RESULT ^= $1021
RESULT &= $FFFF
I already know the string size for DataString, it is the size of the return value of ComputeCRC that seek, because its size will not be a constant size
Looks like you will have to loop around all the bytes in the string parsing out the delimeters, length, value. Maintain a pointer containing the address of the actual start of the value digits. Pass that and the length to the CRC function. I might be tempted to add a routine to FullDuplexSerial that takes a string of bytes and a lenth. like your CRC function..
it is the size of the return value of ComputeCRC that seek
Make life easy, change ComputeCRC to return an actual integer CRC value not a string representation of it. Then just pass that to FullDuplexSerial as str.dec(CRC)
is this question related to the other serial thread? You want some propellers to communicate with each other, right? I don't understand why you want to convert integers to strings for transmitting, read it as string on receiver-side and then parse it again to have an integer. This means lots of extra work for the propeller and for you making the code work.
I'd simply send "binary" data. It's easy to write some functions like txWord or txLong (depending on the range your integers will have) and the corresponding rx functions.
That is just it, I don't want to pass the the CRC value as a string. But I need to know when I have the crc value in a buffer and only the crc value in the buffer.
I don't talk about the CRC, I talk about converting everything to string or not and later on back to integers. A CRC does not care what kind of data is behind. So, you can have a CRC value for a string or for a long.
Someone already provided a solution, but it involves conversion. If I know the size of the data during my reads, I really shouldn't have to do any conversion, just read x amount of bytes and that goes in one buffer, read another amount of bytes and that goes into another buffer. But I need to know the size of my reads for filling the buffers correctly.
'Fill the buffer with pertinent data until
'End Of Text is found
REPEAT WHILE (Char := Receiver.Rx) <> ETX
Buffer[Index] := Char
Index++
To this:
'Fill the buffer with pertinent data until
REPEAT SizeOfData
Buffer[Index] := Char
Index++
'End Of Text is found
REPEAT WHILE (Char := Receiver.Rx) <> ETX
Bruce,
So, you need to insert the length of the string at the beginning of the transmission, including the length of the CRC as ascii? The easiest way around that is to pad the transmission of the CRC with zeros. So as decimal, for example, 00767, always 5 digits to be added to the main data length.
In your example there are three instances fullDuplexSerial. Nothing wrong with that, except that it starts three cogs. If that becomes limiting, look at pcFullDuplexSerial4FC, which runs 4 uarts out of one cog.
If I wanted to transfer a bunch of integer values via FullDuplex Serial I might just send them as raw binary. Four BYTEs per LONG. I'd have a LONG or WORD at the beginning to indicate the length of my message in bytes. I'd have a LONG or WORD at the end to hold the CRC value. I'd have some known BYTE value like STX to indicate the start of a message and perhaps some other to indicate the end. I'd have some hand shaking going on between sender an receiver to be sure this all stays in sync.
Of course that can be accomplished by Sender.Str(STRING("20.2500") but that does not include the indication of the start of a transmission, delimiters, error checking, additional parameters if any, etc.... or end of a transmission.
My goal is to create an object that can pass numerous values, with each value having a error check all during a single valid transmission between STX and ETX.
Comments
John Abshier
Thanks for your response.
Sorry, yes the ASCII representation of the integer.
Bruce
If a function returns anything at all that will be a LONG integer.
In which case it's size is 4 bytes and it's value is whatever it is. Yo can pass it as a parameter to another method directly.
BUT
Perhaps you mean that the returned integer value is actually a pointer, the address of, some string representation of some other integer.
Which do you mean?
So now is that string representation, say "1234", terminated with a NULL byte (like in C) or does it have a byte at the beginning indicating the length (like in Pascal)?
Either way the length is built into the string and you need only pass it's address to the next function. The next function may then need to deal with the length issue.
I want to pass some data to FullDuplexSerial as a decimal value, however I need to know the size of that value to know when to stop reading. For example, when I reach, a delimiter, the next value will be data size, which will indicate how many bytes to read to get the actual value I seek.
<delimiter><data size><value I seek>
Bruce
Actually it would be <delimiter><data size><delimiter><value I seek>
Bruce
pointer := functionCall( ... )
small := temp >> 24
pointer := pointer & $FFFFFF
Where the delimiter is the semi-colon, the length is 3 and the data is 593?
In which case if you have the address of all that as "strAddr" you can use BYTE[strAddr] which will get the first semi-colon. Then increment strAddr and use BYTE[...] again which will get you the length of 3. And so on.
So just put that in a loop and check for the delimeters and values as you go around.
When you get to the value part just pass it to FullDuplexSerial one character at a time as they are discovered. "ser.tx(someChar)"
However, if you have all these nice delimeters in your strings why bother putting the length in there anyway?
Sender.dec(STRSIZE(DataString))
Sender.tx(US)
Sender.str(DataString)
You don't have to explicitly pass the length of the string this way.
Make life easy, change ComputeCRC to return an actual integer CRC value not a string representation of it. Then just pass that to FullDuplexSerial as str.dec(CRC)
The DataString goes to the ComputeCRC befere it is sent to the Receiver(FDS).
Bruce
If I am not mistaken, it does already return an integer instead of a string value.
Bruce
is this question related to the other serial thread? You want some propellers to communicate with each other, right? I don't understand why you want to convert integers to strings for transmitting, read it as string on receiver-side and then parse it again to have an integer. This means lots of extra work for the propeller and for you making the code work.
I'd simply send "binary" data. It's easy to write some functions like txWord or txLong (depending on the range your integers will have) and the corresponding rx functions.
The whole point of the CRC function is the numeric value. It tests the validity of the data.
Bruce
Someone already provided a solution, but it involves conversion. If I know the size of the data during my reads, I really shouldn't have to do any conversion, just read x amount of bytes and that goes in one buffer, read another amount of bytes and that goes into another buffer. But I need to know the size of my reads for filling the buffers correctly.
Bruce
To this:
So, you need to insert the length of the string at the beginning of the transmission, including the length of the CRC as ascii? The easiest way around that is to pad the transmission of the CRC with zeros. So as decimal, for example, 00767, always 5 digits to be added to the main data length.
In your example there are three instances fullDuplexSerial. Nothing wrong with that, except that it starts three cogs. If that becomes limiting, look at pcFullDuplexSerial4FC, which runs 4 uarts out of one cog.
If I wanted to transfer a bunch of integer values via FullDuplex Serial I might just send them as raw binary. Four BYTEs per LONG. I'd have a LONG or WORD at the beginning to indicate the length of my message in bytes. I'd have a LONG or WORD at the end to hold the CRC value. I'd have some known BYTE value like STX to indicate the start of a message and perhaps some other to indicate the end. I'd have some hand shaking going on between sender an receiver to be sure this all stays in sync.
-Phil
1. read = I want to send a string = "Test" the data length is strsize(string("Test")) so for data length read Test into buffer skip over delimiters
go to the next data length which indicates CRC size and then read for CRC size read into CRC buffer.
Now that is an excellent idea.
Because if you do so you can still use other non-hex characters as markers/delimiters for start of message, end of message etc.
If you can tolerate halving the data transfer rate that is.
That's even more confusing:)
Can you write for us an example of how a message will look on the line?
My messages are simple "20.2500"
Of course that can be accomplished by Sender.Str(STRING("20.2500") but that does not include the indication of the start of a transmission, delimiters, error checking, additional parameters if any, etc.... or end of a transmission.
My goal is to create an object that can pass numerous values, with each value having a error check all during a single valid transmission between STX and ETX.
Bruce