How to keep pst.StrIn from stopping a loop?
turbosupra
Posts: 1,088
I feel like I'm over complicating this and I can't figure out a good way to do this, I do not want to give it its own cog, how can this be done?
Right now I have pst.StrIn inside of my main loop and I won't be sending too much data to it, but I'd like to do so without using a dedicated cog for this. I'm also using the main loop to transmit data to the PST and to my GUI as well.
Thanks.
Right now I have pst.StrIn inside of my main loop and I won't be sending too much data to it, but I'd like to do so without using a dedicated cog for this. I'm also using the main loop to transmit data to the PST and to my GUI as well.
Thanks.
Comments
I have a modified version of Tim Moore's four port serial object that alerts you when a new string has arrived by incrementing a counter. I just check the counter against its previously know value to see when a new string has arrived. With a 512 rx byte buffer, I don't worry about the buffer filling up.
I presume you are using the Serial Terminal object. It requires, as does FullDuplexSerial, its own cog to run the pasm section. This is because it is bit-banging the serial and needs to do so with precise timing. The spin code will run in your main cog.
I was reading about your pcFullDuplexSerial4FC in a forum search and when I read that you thought it was becoming quite complicated, I knew it wasn't something I would be able to understand.
I will clean up my code and archive/attach it as you've asked. I am using the Parallax serial terminal object and I'm using it to send values already. To me it seems a waste to have it use a separate cog to receive values also, especially when it will be sending values 99% of the time and receiving them only 1% of the time.
What I was hoping to be able to do is have it loop through sending and receiving, and when there was a byte to receive, it would add it to a byte array and continue on through the loop until the next byte is received, and continue to do that until it received a carriage return indicating the string was complete.
A demo is attached, but the main method is one that you install in your own program, not in pst necessarily. You also need a global byte array of a certain maximum length to receive your string and a global variable to index it. There are many ways to go about this. The following starts with the index at zero, and adds characters to the array each time your program calls it, until a CR is found or until the buffer hits the maximum length. At that point it sets the index to -1, which signals to your program that a complete record is now available. Then your program does something with it and sets the index back to zero so that it can capture a next record.
I will get out my demoboard and try this tomorrow and report back with success or any questions
I want to add some information about how the serial connection works:
the object "parallax serial terminal.spin" starts a PASM-cog that does all the bitbanging details about receiving and sending the serial data.
You could say the serial "driver". This PASM-driver has a send and receive-buffer of 64bytes.
This means you can send up to 64 bytes from a PC to the propeller-chip running "parallax serial terminal.spin" and this driver will keep
this received bytes in RAM for hours and days as long as you don't stop the serial driver or reset switch off power.
If you would send a 65'th byte, the oldest byte would get overwritten by the newest byte. (That's how a FIFO-buffer works First In First Out
If you call one of the methods like "Rx" or
PUB CharIn : bytechr
PUB StrIn(stringptr)
PUB StrInMax(stringptr, maxcount)
PUB DecIn : value
PUB BinIn : value
PUB HexIn : value = any of the receiving methods
All these receiving methods take bytes out the receive-buffer of the driver.
This means if you can make sure that you really don't send more than 64 bytes before you call any of the receive-methods
all bytes will be there. Just the time when these bytes are transferred from the driver-buffer to your variables is later.
If you can't make sure this - you have to transfer the received bytes before the buffer is filled completely.
Here comes in the suggestion of the others. RxCheck checks if there are any bytes in the receivebuffer
if YES RxCheck takes this single byte out of the receivebuffer and hands it over to you
example
MyReceivedByte := RxCheck
Now it is up to you to do the further processing of this byte. (as it belongs to a string = bytesequence with a defined termination-byte))
If you are sending always a string
RxCheck contains the first byte of this string and if you do a StrIn then the bytearray contains all the bytes of this string except the first one.
example-pseudo code
Please describe the most complicated case that could occur in this project.
minimum time between sending two strings to the prop
maximum length of the strings you are sending
maximum time between two runs through your main-loop
global purpose of all these string receiving
If we have this information a suitable solution can be found
best regards
Stefan
Thanks for the reply and FIFO buffer explanation. Can I change the byte array size in the code below (from the PST) and increase its array count to a larger value?
To answer your questions:
I would be sending multiple variable updates via a loop, all at once from a PC serially, such as value1=2[carraigereturn]value2=3[carriagereturn]value3=4[carraigereturn] etc etc
I do not have all of this code completely written yet, but I think I can say with certainty that each individual string value would not be longer than 32 bytes including the terminating 0. I could be sending up to 500 longs/updates (estimated maximum) at a time if I were to estimate (right now it is a much smaller number) to update approximately up to 500 variables that the propeller would be using.
The main loop is also transmitting serial data to the GUI on the PC, if I were to estimate the maximum amount of time, I would put that at clkfreq/20? I would think it would be much much faster, but I'm not sure how much the serial data transmission is going to hurt my speed.
The purpose of receiving these strings is so that I can parse through them and update variables that are already in my code through a GUI and without resending the entire code to the uC, to update value1, value2, value3 etc etc.
As long as the buffer is in cog-ram I guess the size is limited to 256 / 2 = 128 bytes. half and half for send and receive.
There is a derivate of the Serialobject that uses hub-ram for the buffer. This offers buffersizes of as many kBytes to fill up the complete 32kB of HUB-RAM.
Which method is used depends on how often do you send strings from the PC in comparison to how fast you process them in the propeller-code.
simple example your buffer is 10 bytes long
every minute 2 bytes are sended from PC to prop. This means a buffer of ten bytes is filled after 5 minutes.
if you process ALL the received strings surely every 4 minutes the concept of just process the bytes when the loop has time for it will work.
If you process the strinsg only every 6 minutes some bytes would get get lost as after 5 minutes the oldest two bytes get overwritten by the newest two bytes.
In this case it will be better to have a method that is checking the receive-buffer very often and when a byte is received transfering the byte to a variable as soon as it is received.
This will keep space free in the buffer for further bytes.
So it depends on how often do you send bytes and how often do you process them.
What you have described so far isn't the REAL global purpose. What does the propeller do in the end?
Lighten up a chrismas-tree with light-patterns? Making an mechanical easter-rabbit stepping in the floor dropping eggs? :-) :-)
I don't know. If you are willing to share the real global purpose of the whole project please describe it.
As soon as this information is here I'm sure that new solutions to the problem can be found.
Example to what I mean:
imagine a small tube. Diameter one inch 6 feet long sticking solid in a cement-ground. Something small dropped into this tube.
Diamater 1 inch is way too small to put your hand into it. 6 feet is way too long to grab it with your fingers.
Now you are asking for a mechanical three-finger-telescope-arm with a glasfiber-optic to get light into the lower end of this tube and then trying to grab the object with these mechanical three fingers.
Or even worse you try to build this three-finger-grabber DIY and you are asking on a even more detailed level what's the G-Code for milling oval wholes into aluminium?
If you provide the information its a small part out of wood and the tube is vertical - an altenative solution could be simply fill water into this tube until the object swims up!
Now you might answer no this will not work it's out of steel. OK why didn't you say that earlier? We just glue a magnet at one end of a rod of wood 8 feet long
and voila! there it is.
Ah no its stainless steel that's not magnetic and the tube is horizontal . OK so let's take something adhesive on the end of the rod of wood.
Got the principle of what I'm trying to say? So again if you don't mind to share the whole project and the role the strings are playing in it please describe it.
best regards
Stefan
None of the usual serial objects use cog RAM for a buffer. They all use hub RAM (PST, FDX, Tim's 4 port).
I suppose it's possible to use cog RAM as a buffer but I'd think it would be very awkward for other cogs to access the buffer.
This were you'd change it.
As long as you have room left in hub RAM you could even make the buffer larger. 512 or 1024.
If you don't remove characters from the rx buffer in PST or FDX in time and the buffer head catches up with the buffer tail, you'll lose all the data in the buffer (the methods will think the buffer is empty instead of full).
I should have been more detailed and would have, had I known it would help. Excellent analogy though
This project (as most of my other projects) is automotive related. I had the prop controlling some solenoids on my car based on some analog inputs such as pressure sensors, a few digital inputs such as the crank trigger wheel and some variables whose value I want to be able to change through a GUI I wrote, instead of re-flashing the prop. So if I felt a value should say be 5000rpm instead of 4000rpm, I would type "5000" into a listbox, and then press a button which would send the data to the propeller serially. Behind the scenes, my GUI would send "[specific variable name here]=5000" and now the propeller would process the serial data, update the variables value and react to data concerning that variable name, based on the value of 5000 instead of 4000. Does that make sense? I wouldn't be sending data to the propeller that often, only when I am "tuning" things.
On the transmission of data from the prop, this would be happening quite regularly. Any time my laptop is connected, I will be sending data from the prop to the GUI on screen and displaying it. It will also tell my GUI when it has enabled out disabled outputs and the readings it has calculated from analog sensors.
Is that detailed enough? If not, I can show you the GUI via Skype if you'd like.
(b_MyReceivedByte is a 100 container byte array)
We're just using "result" as an index variable. Sometimes the receive line will pickup no activity as all zeros if there isn't a pull-up resistor on the line so I excluded zeros as well as negative ones.
If you want to see the value of a byte with the "hex" method you want to use two digits. Your original code displayed the last digit of the memory location used by the first element of your array.
The "str" method needs the address symbol "@" since it needs to know the first location of the characters (bytes) to be displayed. It will display (or try to display, some byte values aren't valid display characters) the bytes starting at location "@b_MyReceivedByte" and continue displaying characters until it finds a zero.
The "hex" method works a lot like the "dec" method. You just pass it the variable you want displayed and how many digits of the variable. As I mentioned a byte needs two digits to display a word four digits and a long eight digits. I included a couple of lines to display the location in hub RAM your "b_MyReceivedByte" array starts.
Edit: The above code uses a byte variable when a long is needed. See kuroneko's posts below.
I used hex just because it says in the method it returns a hexadecimal value, not for any other reason. I see the sentence is still there, but I thought I'd post what I'm seeing.
The output from this kind of looked like the output mine was giving when I had it set to pst.str
Brad please post your complete code as a zip-archive created by the propeller-tool.
The value of variable b_MyReceivedByte increments with each loop. I guess this is not how it should work
Or Duane you have to explain a bit more what you are trying to do with this code.
first of all thank you Duane for correcting me about cog-RAM. Yes of course it must be HUB-RAM. Cog-RAM is cog-exclusive.
Other cogs can't access it.
@Brad, Yes this is mostly detailed enough. So the prop is only receiving data for tuning values from time to time.
If you do this while your car is at home in your garage even waiting for receiving a string seems no problem.
Somehow it seems to be a problem. Can you describe why it is a problem?
best regards
Stefan
It's not that complicated to use. I use it as my standard serial object. If you are interested I can post a very simple demo program with added comments
The problem is that when I have the code using the pst.StrIn method, it will not allow the code to continue to loop but instead hangs the code and waits for input values, similar to what a waitpeq would do if you never received that state at all, the cog would be paused and not do anything. This is what is happening, my code will run through each line until it gets to the pst.StrIn line and then will sit there until it receives a string.
I want it to cycle through performing the normal prop to pc transmission, and when it receives a pc to prop transmission, to queue each byte of the updated command until it receives a carriage return and at that point, pass the byte array to another method that parses the update command and then updates the actual variable with the value, for example rpm=5000. I want pst.StrIn to queue the r, p, m, =, 5, 0, 0, 0 and the carriage return terminating 0 for a total of 9 bytes. Then realizing there was a terminating 0, to pass the byte array to a parse method which then passes the variable name and variable value to an update method.
I don't want to use an entire cog just to have it monitor the pc to prop transmission and so I'm trying to figure out a way to use pst.StrIn in a way that it does not pause the entire cog that I am using to send data from the prop to the pc, and prevent it from looping through the code.
Edit: The above code uses a byte variable when a long is needed. See kuroneko's posts below.
b_MyReceivedByte isn't incremented, the index to the array is incremented.
The code (untested) should display the hex value of the last character entered followed by all the characters in the array so far. It also displays the starting memory location of the array.
Edit: The code by itself isn't very useful as is. It's meant to be an example and I just wanted to modify Brads code without making too many changes(I don't think I keep this last goal).
By the way, nicely done, thank you!
I'm pretty sure it's not the tab character (IIRC it's 9). The Prop was picking up all ones. A byte of all ones is "$FF" When you send "$FF" to the terminal you get "ÿ". If you use a VGA or TV terminal object you'd see infinity signs instead since the Prop's $FF character is the infinity sign.
I noticed I left the index in the command to print the address of the array to the screen. So the address of the last character added to the display is shown, not the address of the beginning of the array as I had previously stated.
Thank you for the code. For my own learning experience, can you tell me why byte b_MyReceivedByte[_byteLimit] works under the limited testing I did but will not work well in the long run? It is a byte array, does that make a difference? I chose a byte array because it allows me to have a string with approximately 100 bytes in it. This is also why I chose the bytemove. I'm sorry I didn't include that in the original code, I thought that was included.
With the following code:
b_waitingToBeParsed[byteMoveIndex++] := b_MyReceivedByte
It appears that only the first byte of the long b_MyReceivedByte is copied to that particular byte array container (b_waitingToBeParsed[byteMoveIndex]), have I understood that correctly? The remaining 3 bytes are discarded whether they hold non zero data, a null value or terminating zeros? And then that single byte is added to the incrementing byte array?