FullDuplexSerial - receiving and displaying bytes
xanatos
Posts: 1,120
I've been racking my brain trying to figure out how to get FullDuplexSerial to receive some bytes sent from a Basic Stamp BS2px, and just (for now) display them via the PST window.
In searching through probably over 50 posts so far, I'm as confused as ever... but I found a post ( http://forums.parallax.com/showthread.php/140157-Storing-and-Using-Byte-Received-with-FullDuplexSerial ) in which there was some very helpful info - but I'm still not quite able to make this work.
Here's some code as it stands now (with extraneous stuff deleted - let me know if you need to see the full code - but be forewarned - there's a lot of it!)
In the BS2px:
And that works just fine and I've verified with an LCD that the SEROUT is working. No issue there.
But in the Propeller:
The 10 setup for EMArray was arbitrary since for the purposes of getting this started I am only using EMArray[0] - once I get that going I'll set that up accordingly.
Two issues with the code I've got now - First, I'm unsure how to pace the fds.rx with the BS2's SEROUTs - or if I should read in ALL the 1 to 127 tankNums into an array, then parse the array out (assuming I have enough longs left to do that) - or if I should set up a handshaking/flow control arrangement to read one SEROUT in the loop at a time.
The second issue is that the above Spin code is contained in a loop. It hangs after about 6 iterations of the loop - as I'm sure you can see from looking at the code piece there :-) If I just comment out the EMArray[0] := fds.rx line, the loop runs through to completion, but of course just prints zeros to the PST window.
This is - obviously - my first time working with FullDuplexSerial, as is just about everything I've had to learn on this project.
Thanks for any corrections/directions you might be able to provide.
In searching through probably over 50 posts so far, I'm as confused as ever... but I found a post ( http://forums.parallax.com/showthread.php/140157-Storing-and-Using-Byte-Received-with-FullDuplexSerial ) in which there was some very helpful info - but I'm still not quite able to make this work.
Here's some code as it stands now (with extraneous stuff deleted - let me know if you need to see the full code - but be forewarned - there's a lot of it!)
In the BS2px:
FOR tankNum = 1 TO 127 DEBUG DEC3 tankNum, CR SEROUT SpinRX, T9600, [DEC3 tankNum] NEXT
And that works just fine and I've verified with an LCD that the SEROUT is working. No issue there.
But in the Propeller:
VAR byte EMArray[10] OBJ pst : "Parallax Serial Terminal" fds : "FullDuplexSerial" PUB Main fds.start(22, 27, 0, 9600) repeat 127 fds.rxflush EMArray[0] := fds.rx pst.dec(EMArray[0]) pst.str(string(" ")) pst.str(string(13))
The 10 setup for EMArray was arbitrary since for the purposes of getting this started I am only using EMArray[0] - once I get that going I'll set that up accordingly.
Two issues with the code I've got now - First, I'm unsure how to pace the fds.rx with the BS2's SEROUTs - or if I should read in ALL the 1 to 127 tankNums into an array, then parse the array out (assuming I have enough longs left to do that) - or if I should set up a handshaking/flow control arrangement to read one SEROUT in the loop at a time.
The second issue is that the above Spin code is contained in a loop. It hangs after about 6 iterations of the loop - as I'm sure you can see from looking at the code piece there :-) If I just comment out the EMArray[0] := fds.rx line, the loop runs through to completion, but of course just prints zeros to the PST window.
This is - obviously - my first time working with FullDuplexSerial, as is just about everything I've had to learn on this project.
Thanks for any corrections/directions you might be able to provide.
Comments
You also need to start PST.
Serial objects require an accurate clock. Make sure you're setting the clock mode.
Here's the same code using only PST.
Alternatively you could use two instances of FullDuplexSerial.
PST is started and working and clkmode & xinfreq are set. I haven't shown my setup for PST because it's working and displaying a lot of stuff from other areas in the code I have running. Here's my OBJ block:
Everything else in here is working perfectly - better than I could have hoped given my very limited understanding of Spin - thanks to some folks on these forums.
For clarity, here's the selected setup lines extracted from all the other code:
All I'm needing to do here is read the bytes sent from the BS2 into some sort of variable/array, so that I can then place those values into the appropriate locations in an email that gets sent out. Writing to the PST is only for me to verify that I have actually gotten the real data, and the loop counter that the BS2 runs as a data source is only just a test setup to provide a known data stream via SEROUT so I know for certain when I see the data that I have the BS2's actual data.
Here's a segment of code from within the email routine (which works perfectly with the exception of reading any BS2 data with FullDuplexSerial at the moment). Note within the code my block of comments describing what I am actually trying to do and where:
So to sum it up in the shortest possible description - read in SEROUT data from a BS2 in the form of three bytes, for each successive iteration of a loop in the Propeller's Spin program, storing those bytes temporarily for placement in the body of an email.
Everything else is working, I'm just not seeming to grasp how to use FullDuplexSerial in this capacity, and I'm also not sure about what, if any, flow control or handshaking needs to happen between the stamp and the Propeller's serial lines. Currently I just have the stamp wait until the Propeller tells it it's time to start SEROUTing...
Thanks for any help. I know I'm close but ... not close enough.
Dave
PS., In terms of saving memory, once this is all working, I'm taking PST out entirely anyway - commenting out the 100+ pst.str lines that are in there now and commenting it out in the OBJ block, just in case I need it at a later date :-) Only FDS will remain active.
EDIT: You should pick flag bytes that most likely won't be in your incoming data string in that sequence.
As with most programming tasks there are many ways to receive data and parse out the various values. One method I've used is to monitor the incoming data from within the PASM section of the serial driver and increment a flag when an end of message character is received. My main loop can then wait until a complete data packet has been received before parsing the message. Here's a link to a version of a multi port serial driver using this end of message flag technique.
I've also modified Tracy Allen's improved four port driver to included an end of message flag. If you're interested in using this technique yourself, I'll find this improved version, clean it up a bit and post it if you'd like.
I think the more common way of parsing data is to just read it as it comes in. You'll still need beginning and end markers and you'll also need some way of separating the data fields. It looks like you're using commas slashes and semicolons as field separators. Or are these field separators part of the entire message you wish to forward?
Now that I look a bit more at your code, it looks like you just need to receive two values from the BS2? Since it appears thewe are being sent as ASCII characters you still have the need of a start of message character(s), flied separators (unless data is fixed length) and end of message character (optional if data fields are fixed width). You'll need to add these characters to your BS2 output code.
I just noticed JonnyMac's post. An alternative to using field separators is to send the data with a fixed field length. You'll still need some sort of identifier to mark the beginning of your message.
If one doesn't use ASCII characters (or a fixed length message) then the size of the data packet is usually sent as part of a header section of the message. Dynamixel servos use this type of message packet. This has the advantage of being able to send a byte sized value in one character. Ratronic has a object in the OBEX (I've used many times) to communicates with Dynamixel AX-12 servos that illustrate this type off protocol.
Congrats on making it so far!
One trouble with FDS is that its receive buffer is too small, only 16 bytes, and that buffer becomes overrun by data from the BS2SX faster than the Prop is taking it out for retransmission. Take Duane's advice from post #4 and use two instances of PST.
The default buffer size for PST is 64 bytes, which is better, but you can change it to get a little more leeway in your timing. In the code listing for PST you will find the buffer size defined in the CONstants section, like this. If you have Prop memory to spare, you can change that too 128 or 256. That will take space for both instances of PST, and PST does not allow for separate values for the rx and tx buffers.
In your original spin code, there is no need for rxFlush. That is trashing characters that are arriving from the 'SX. Also, there is a mismatch between what you are sending (3 ascii decimal digits per tank, fixed fields, no separator) and what you are receiving on the Prop (1 byte fds.rx that you print out as a decimal value). If you can fit the tank values in individual bytes, that would in fact allow the entire record from 127 tanks to fit in a 128 byte serial buffer, so the Prop could take all the time it wants to process the packet.
I took out my old QuickStart board, loaded it with FDS, PST and then wrote a little looping program to take the BS2 data in, and I just sat on it and tried stuff shotgun style, until it started to make sense. I just logged on now to report my success and I see that several of you have been enormously kind in offering assistance, and I can't begin to tell you how good it feels to feel like I wasn't working without a net after all.
I did discover remember (since I did set that var up to be a word in the stamp code) that my level data was really going to be a word, not a byte, and that was part of my problem.
The BS2 doesn't transmit characters as ASCII - that much I knew. But I did have to bit-bang the two bytes back together to reconstitute the original number.
When I run the two codes below, I get the real live numbers out on my PST window - including the correct, reconstituted TankLevel value.
WOO HOO!!! Lord, that felt good! :-)
I'm sure that there is probably a much more efficient way of doing this - but dang it, it works and I actually understand it! :-)
BS2 test program:
Propeller Spin Code (a lot of stuff is commented out - remnants from the original sections from whence it came):
You can't imagine the relief. This has to be fully done, tested to the ends of the Earth and installed by July 15th. This was the last piece I wasn't getting to work. While I have to play a little with getting the start points of the data to synchronize a little better - that's the easy part.
**** THANK YOU ALL AGAIN, KNOWING THAT THE HELP IS THERE IS IMMEASURABLY REASSURING ******
Some questions will follow as soon as I finish celebrating! :-)
Dave
Tracy - So I don't need fds.rxflush? The incoming data will write over the old stuff with no possibility of corruption, correct?
Also, the way I'm doing this comm between the stamp and the bs2px is that I have the propeller request one line at a time - I have some flags set to toggle pin states between the two devices. The stamp can possibly be busy doing a scan loop of all 128 sensors, which takes about 8 seconds, so I have the stamp set a pin high when it has seen the request for the data and reports ready to send. Then the propeller tells it to send and resets the flag, so when the BS2's loop comes around again, it holds before sending the next data line. This process continues until all 128 data lines have been loaded.
Is that dumb? :-) Is there a less "brute force" way of doing that? It goes pretty quickly by human standards (takes about 3 seconds for 32 scans, so I'm guessing 12 seconds for all 128.)
In any case, in this way I only use three bytes plus a word to reconstitute the level value back to human-readable original, on the fly as I build the body of the email. There doesn't seem to be any issue with buffer size or timing by doing this. Am I missing a possible future gotcha?
Ratronics - reading your post regarding flag bytes - that sounds like it would help me make sure my data received is properly aligned, especially if I go with some sort of array that reads all 128 data lines at once (that would be three bytes per data line actually, one byte for the tank number, and two bytes for the level data, plus a byte for the flag byte. But then my question is - since one of those three bytes (tanklevel.byte0) can be anything from 00 to FF... how can I make a unique byte flag? :-)
More questions I'm sure, but for now I can breathe again! Now to integrate this with the "real" program :-)
THANKS!
Dave
PS., I find myself astounded at how simple and obvious some of this stuff looks *once you're on the side of understanding*. I hope I never forget how it looked to me before I made that transition. It's like being in a different universe.
the FullDuplexSerial object only has a receive buffer of 16 bytes. You don't have to use 2 bytes for your flag it could be more to make it more unique. Good luck it looks like your are close to achieving what you are trying to do.
There are other ways to check alignment. For example, you expect bytes 0,3,6,9,... to be an increasing sequence with the tank number. The receiver can check and insist on the strict progression. Or, to get a unique alignment tag ($FE), you might encode the level so that the bytes transmitted will be in the range 0-127 instead of 0-255:
levelByte0 := level // 128
levelByte1 := level / 128
level = levelbyte1 *128 + levelByte0 ' allows levels from 0 to 16383.
The Stamp has flow control capabilities:
SEROUT SpinRX\flowIn, T9600, timeout, [tankNum, TankLevel.BYTE1, TankLevel.BYTE0]
That is kind of like the flow control you want to implement in the program loop.
Thank you - I had completely forgotten that the stamps can do flow control in the SEROUT/IN statements - never needed it before. Implementing it now.
As for alignment, since the tank number should always match the loop count, I'm just having it check to be sure the received tank number == the loop count, and if not, re-request that data line, otherwise, proceed to the next. Simple and since speed isn't really a requirement here, relatively bulletproof.
92 in the shade here today. Good day to be in programming!
Thanks again,
Dave