Receiving Strings and Writing them to an Array, Question ...
JMLStamp2p
Posts: 259
I want to receive Two ASCII characters and Save them to an Array called rxStr_array. After Two characters are received I want to Transmit those characters out the serial port ...
Can someone please let me know if the following code is the correct way of accomplishing this task. If it is not please explain what I am doing wrong.
Thank you,
John Michael
Can someone please let me know if the following code is the correct way of accomplishing this task. If it is not please explain what I am doing wrong.
Thank you,
John Michael
PUB rxStr(num_of_chars, strPtr) | char, index 'strPtr holds the 16 bit location of the rxStr_array. 'num_of_chars is the amount of characters we want to receive. index:=0 'Set the index pointer for rxStr_array to location "0". repeat 'Repeat the following ... char := serial.rx 'The Variblle char is set to the Character that is received into the serial port. if num_of_chars == 2 'Check to see if we have received 2 ASCII characters, 'If not Loop again until we have. byte[strPtr][index]:=char 'Write the character to the array at the index position. index++ 'increment the index or position that will hold the next character received. if num_of_chars--==1 'If we have received the Total number of characters ... Transmit_Timer_Values(@rxStr_array) 'Transmit the characters that is in the rxStr_array. serial.rxflush 'Flush the input Buffer. num_of_chars:=2 'Set the Total characters to receive back to "2".
Comments
It pretty much depends on what your goals are to give you the best possible code. For example in your implementation
Transmit_Timer_values( @rxStr_array )
you used rxStr_array directly ... so, why do you pass it as parameter to the function? Why do you pass num_of_chars if your code can't properly handle different values?
You see the difference?
My code still cannot tell the difference between Go and stop though. Code is on another computer, I will post it later this morning to get your advise.
Thank you,
John michael
The Propeller is receiving the Byte but turns on LED_0 no matter what Character I send. Need some Help to accomplish this task, I don't really understand how to terminate the string correctly and need
to see a small Bit of example code so I can better understand it. My code so far is:
if dataIn[0] := ("A")
This is an assignment and meanwhile it is a bug ;o)
if dataIn[0] == "A"
would be the correction for that.
BUT ... why ??? Use parameters to have some more flexible code and use math
Then you can remove the whole if by of course this needs the LEDs to be in a row. If you want to be flexible you can setup a lookup-table to get the right pin number from an array.
Depending on the reliability of the input-data you can have some safety checks before. For example:
In the LED_0 and LED_7 functions you call Receive. This is called a recursion because you call LED_x from Receive and then Receive from LED_x. This will eat up your stack-space. When the end is reached strange things will start to happen as you overwrite code then. From functions you should somewhen return.
Further explanations:
if you stored an "A" in dataIn[0], the calculation dataIn[0]-"A" will simply give you 0 (zero).
if you stored a "B" in dataIn[0], the calculation dataIn[0]-"A" will simply give you 1 .......
There is no need to have 2 if statements (and 2 LED-functions) if the rest of the code can deal with these calculated numbers.
I am just learning Spin so I am still trying to understand how to write code :>) When I use the rxStr function to receive a "byte" in the serial port via Extended_FDSerial, is the byte received already written to the dataIn array and the pointer incremented? Am I trying to insert un-nessessary code in my function?
Do I need to just Terminate the string in my code after a certain number of characters are received? Not sure how to write my receive code to test for a certain number of characters and terminate the string correctly.
Extended_FDSerial statement:
Accepts a string of characters - up to 15 - to be passed by reference
String acceptance terminates with a carriage return or the defined delimiter character.
The following is my complete code so far "as small as it is", the Propeller sends the correct commands to the TeLit Cellular engine and Transmits the message "System is Initiated" to my Cell phone. I want to return either "Go" or "Stop" back to the Propeller and have it turn on LED-0 or LED-7 on my Professional Development Board. The PUB INITIATE function is working correctly and sending the message, just having a Bit of trouble understanding the correct procedures for the receive function. If you could Help with this it would be Greatly apreciated.
The one in the object reads a string until you entered CR or until you have entered 15 characters. So, if you enter "A" and want the code to react immediately it's the wrong function. Dito if you don't want to enter CR. It really waits for 15 characters or CR.
What's wrong with rxStr from post #2?
Is "A" go and "B" stop? And why do you want to read 2 characters? What is the purpose of the second character? In your code you only checked the first one. I'm sorry, but I am totally lost in all your questions, you should simply show what the expexted input looks like and what should happen in this case, for example:
A<CR> -> light LED 0
B<CR> -> light LED 7
AX
go<CR> -> start blinking LED 0
stop<CR> -> stop blinking LED 0
go
stop
????
I want to use Extended_FDSerial: PUB RxStr (stringptr) : Value | ptr or whatever is needed for the following ...
I want to either Text the TeLit the word "Go" and have it Turn on LED_0 for one second or Text it "Stop" and have it turn on LED_7 for one second.
The reason for the second character is that I will have many commands that I want it to respond to in the future and therefore need to understand the whole parsing thing. Right now I just want to understand how the Code should be written for these two commands.
All commands in the future will be less than 15 characters long, therefore ...
go "Blink LED_0 for one second"
stop "Blink LED_7 for one second"
Thank you for taking the time to Help ...
John Michael
If you want to have an expandable command-line interface, then it's good to read the whole command first. If you later on have commands that need parameters it's good to read the whole line first and have a general parser which processes the whole line.
You could for example have a look into the ConfigReader-object I put into the tools-section of the object exchange. It currently reads a command-line from a file, but it can easily be changed to read from serial interface instead.
I think a good idea - also used in Mike Green's basic - is to handle commands by converting it with a hash function into a long-value. Then you don't have to store all the command-strings and you don't have to loop over all strings to identify the right command. Calling the right function is simply a case statement which compares the hash-value of the command just entered with the pre-calculated hash-values of the existing commands.
* Use the RxCheck function which does not wait forever for characters to be received. This lets you timeout, if your comms link is broken for instance.
* The RxCheck needs to be inside the loop where you are waiting for command characters. ie inside the Repeat loop (or I guess a different cog but that complicates things too much)
* See the code example I posted for how to process single character commands using the RxCheck function. That code I know works
The method also works for single characters.
If no match is found the method returns number of strings plus 1, in this case that would be 7. The first repeat in the method should be Repeat number of strings. The returned index value could be used in a CASE instruction to control your LED's
It needs a little work but might give you a few ideas
Jeff T.
Greatly appreciated,
John.
I did not run the code, but from my analysis I'd say it only works when some conditions are met - which is not good!
You always want to read 10 bytes max., but actually if the user does not enter the next character within 10ms the user has to restart input. The preferred solution which is used since centuries ;o) is, that the user can input as long as he wants and when he's done, he simply hits the ENTER key. This is by the way what the extended FD function rxStr is doing for you - no need to reinvent the wheel.
In str_search ... why do you limit the number of commands to 6 ( repeat 6 ). Your second 0 at the end of the list would be a perfect marker for EOL (end of list). So, you would stop the outer-loop if a pointer points to a zero-byte instead of to the next command. This way you can easily extend the command list without changing the number.
Why do you copy the command into a buffer for comparison? As each command is 0-terminated you can simply compare the strptr directly with the string stored in my_cmds.
The return value of 0 is used whenever "command not found".
And again ... with a hash-algorithm you would save memory (16bytes in this easy example) and runtime as hashing with a case-statement is much faster than looping over all command-strings.
PS: sorry for that, but my code is untested because I'm currently at work and I don't have more time to spend even if I have my QS with me ;o)
PPS: Brain-debugging showed me that the ptr++ was missing (marked with "fix:" in the comment) - having a good breakfast seems to help ;o))
I did it this way as a preliminary because I did not see where there was any kind of ETX and it was intended to take care of a pre defined string attached to a "speed" button. If the string should have a newline,carriage return or a zero that would indeed be better.
From my point of view I was not particularly happy that "main" continually looped through the whole procedure, I would prefer a STX to trigger the events, but I typed it up this way because I thought it would broadly cope with the current situation.
The second zero was an after thought placed there just before I posted, and I agree it is a better solution.
I think the main point is to keep things modular and portable to other applications, str_search is looking pretty good right now.
Jeff T.
EDIT: I don't know what a hash algorithm is so I do what I can with what I understand, but it will be something to read up on.
In the last two cases I'd suggest to use the ENTER-key approach instead of an automatic timeout!
Ok ... let's go on with learning!
A hash function simply takes something as an input - for example a string or a bytestream (let's simply call it a stream) - and somehow calculates for example one LONG value for that input. So, if you input the same stream twice, you will get the same result. A good hash-function will spread the result over the whole number-range of a long even if you only have little changes in the input-stream.
So what is the purpose of this?
It's exactly to help with problems similar to the 'find the right command'-task. Let's assume you have to store a number of random words coming from ... wherever ... in an fixed array of strings AND later on you would like to find these words as fast as possible.
You could easily store one word after another, but when you have to find the words you have to scan the whole array.
You could store them one after another but sorted. When you have to find the words it's easier because you can use binary search. But when inserting a new word you might need to move the whole array to keep it sorted.
With hashing you simply have a function which calculates the index out of the word itself and simply store the value there. No sorting and easy to find.
In detail there is a little problem because two different words could create the same index. In this case you have to re-hash for example by calculating another index using a re-hash function or by an algorithm like .. if the index is already occupied by a word use the next index ...
But even then you have less to do in average in the end compared to the other ways to deal with this kind of problems.
Ok .. here is some code from a command interpreter I have:
So, this is effective as you only loop over the key-input once to create the hash-value and there is no need to store all the command-strings themselves.
PS: Maybe I should have used the word key instead of stream because that's how hashing is used most of the time. You have a hash-map for storing values by using a unique key to identify each value.
For our problem storing is not necessary at all.
I think due to my in-experience I have complicated things, I have never even heard of half the things you are talking about :>) Can you give me a Very simple example of the Best way to accomplish this?
John.
! = Attention
C = Command
1 = Data
<cr> = carriage return (0x13)
When you see an "!" you know the next byte is an identifier followed by the data which ends with a <cr>.
I bet I have tried 100 different ways to do this and nothing seems to work, new at Programming in Spin :<(
John.
Questions:
1. Do you prefere the slower "find out by yourself"-approach where we only give you some hints and you still have to fiddle some things out by yourself?
This is the way I would prefere because it is the way where you learn most! Doing your own mistakes and solving them is confirmed by neurologists to be the most effective way of learing.
2. Dou you prefere the fast way "let us deliver the solutions which fit ... say 99% to your problem". This I'd only suggest if you have some time-constraints to meet in your project.
Of course you can also learn your lessons this way, but it does not leave the same traces in your brain and maybe a half year later you will ask the same questions again not remembering this thread. ;o)
As long as I don't have the answer, let me continue with some general concepts of technical problem solution.
My understanding of your problem is, that you are the master of the protocol send from the cell phone to the propeller. So, actually the question is: which protocol makes sense here.
Choice a): a pure technical protocol. You can send binary data, which is more effective than command-strings. For example you could send
0 -> go
1 -> stop
plus 254 other commands
Choice b): a human readable format.
The advantage of a) is that you can send more commands in the same time and you don't need conversion for example from string to long-values as you send it binary. But ... you have to be sure that sender and receiver do understand correctly. You can not easily have a look at the traffic by yourself for debugging/analysis or for replacing the sender.
The advantage of b) is that you don't need the sender to be implemented when starting with the receiver. You can for example simply read the input you send to the program via terminal-program (like the parallax PST). This splits the problem in two independend and smaller parts which is therefore easier to implement. Even later when you attach the receiver to the real sender you can listen to the data and easily inspect it.
So, what's the prefered solution for your problem - my guess is b) because I don't see bandwidth constraints or the need to send 10,000,000 commands per second.
Ok ... let's make the receiver run with a connection to the PC using the PST first.
So, you already mentioned the Extended Full Duplex driver which has the rxStr function, right? Simply use that to read the input into a string-buffer.
Then you can pick the hash-function code from my last post and call it with a pointer to that string-array.
For the beginning you can simply output the opcode back to the PC using the hex-function of the full duplex serial driver to see what hash-codes are produced with wich commands.
Then you can start implementing the case-statement also given in code of my last post to call your actual go and stop code.
Viola ... first step accomplished! I can post some more code in the evening - when I'm back home - which will also parse parameters in a nice way and give you an own implementation of the rxStr which
a) has a bigger input-buffer (because for commands having parameters 15 characters seems to be quite small)
b) already prepares some things during input to make parsing easier
So in the end you can read commands with up to 10 parameters which can be
46577 - decimal input
%010101 - binary input
$AF45 - hex input
aslfjlslfjljk - unquoted string
"sldfj slfjklsj ljsfkj" - quoted string which allows having spaces and unparsed numbers
All parameters are stored in an parameter-array where all number format inputs are already converted to long and for strings you will find a pointer to the string in the key-buffer.
Commands:
LED16
LED17
LED18
EchoOn
EchoOff
I am more of an Artist than a Programmer and a Very Visual person. I have enough Tech. in me to really want to understand the in's and outs of why the code works so my answers to your questions are as follows:
I would prefer to take the Receive code a line at a time and "Comment" what I understand it to accomplish; not only for myself but for others to benefit as well. If my understand is not correct, please make adjustments to the comments so they will help me understand. I would like to use "Text" as a communication medium for this project so that "non-Tech." individuals can operate the interfaced equipment from their Smart Phones. If they want to turn on a water pump, they simply just text "Turn on the water pump :>)
I beleive that a person retains things better from the thinking process as we "re-wire" our Brains continually but have also learned this: If I try to teach a person to play the Guitar by just teaching them note's and scales I'll most likely bore the "Real Artist" to death until they just give up. The Left Brained guitart player may stick with the notes and scales but never really incorperate a Stylish flare into their playing and never really "Hear the Music". If I teach the "Real Artist" a Song they like that incorperates the same notes and scales they will play forever and learn without really thinking about it :>)
My point, I am that "Creative Right Brained" type that needs to learn by Having Fun a step at a time with the project or the whole thing becomes a Bore ...
Thank you for your patience as I work through the notes ...
John.
Mike G:
Thank you for your posted code, I will try it this afternoon when I get home.
Much appreciated.
I translated it into the version which uses hashing. Of course due to the command-length and the small number of commands it's not smaller because there is the cost of adding the hash-function. But even with this little amount of short commands it's only 3 longs bigger.
I will go on with this code and add the other features I talked about in the earlier posts. The LED commands are a perfect example where I'd use a command parameter solution.
PS: The only thing added to the functionality is that in case of an incorrect command, the error-message also gives the hash value of the entered command so that you can use it in the code.
A couple of suggestions:
1) Post error free code when asking for help. You code does not compile.
2) Post all your code not just the part you think in causing the issue
3) Open up the Propeller manual and read about the Spin methods your are invoking
4) Go through the Propeller Education Kit. The PE kit has a lot of great examples.
I believe this is what you are trying to do...
Thank you for your suggestions and you Code, I'll give it a whirl ...
John.
I am looking over the Propeller manual as well ...
John.
Thank you for your Help,
John.
I see some conditions in this code which propably work for the current problem, but you should know about them if you want to use this piece of code somewhere else or if you do some small changes (increas the number of blinks) and find out that the code no longer works.
In the getBytes-function you call Toggle which only blinks an LED. But the toggle also waits, which is not the best thing to do if you have a function that should receive bytes from an external device. You wait for 1/8th of a second which is a real long time for a controller. With the 115200 bits per second you can receive 1800 bytes in this time which is slightly bigger than the buffer available in the FullDuplexSerial.
So, I would use a different way for just blinking the LED. At the beginning of getBytes you could setup a counter and let it blink the LED. This also makes the getBytes-loop much easier, as you onls need to say
For the counter-code you can have a look at the PE Kit Labs.
However, I will not write the song for you. The music has to come form you. The reason I'm making this statement is once again you have posted code that does not compile which means you did even try to play the song.
Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime
Strings seem to trip up a lot of folks. A String is an array of bytes that end with a zero. Take a look at the string methods in the Propeller manual as well as the BYTE type.
STRCOMP
STRING
STRSIZE
Thanks for your patience and your help,
John.