ComRx_StrFind2 c [D>=29]
Match a received string to a table in NVEM. This is subtly different to ComRx_StrFind
, which uses a different table format.
This instruction seeks to match the data at the head of the default serial Rx buffer with one or more "canned" strings stored in NVEM. Its primary use is for detecting received commands and machine generated binary coded messages.
If argument c is zero, the instruction treats upper and lower case letters as equivalent. If c is non-zero, the case of the received string must match exactly the string stored in NVEM.
The data in NVEM must be stored in a very specific Count/Data format.. This is best illustrated by an example. Lets say we want to detect which fruit has been requested, typed in on a terminal emulator and ending in a carriage return ('0D or 13)
1. NVEM: Message list
First you construct a list of all possible receive strings in NVEM. Each string must be preceded by a byte containing the length of the string itself.
strOrange: NV0Byte 7,"Orange",13 strApple: NV0Byte 6,"Apple",13 strBanana: NV0Byte 7,"Banana",13 strLemon: NV0Byte 6,"Lemon",13 strPear: NV0Byte 5,"Pear",13 strGrape: NV0Byte 6,"Grape",13 ; Byte count ___________^
2. NVEM: Pointer table
Next you construct a table, in NVEM, of pointers to the individual strings.This must be terminated in two 255 bytes. The 255s tell ComRx_StrFind2
that it has come to the end of the table.
FruitNames: NV0Ptr strOrange ;Entry #1 NV0Ptr strLemon ;Entry #2 NV0Ptr strApple ;Entry #3 NV0Ptr strPear ;Entry #4 NV0Ptr strBanana ;Entry #5 NV0Ptr strGrape ;Entry #6 NV0Byte 255,255 ;<<<<< Essential!!!
You will notice that the order of entries is different to the order of the strings themselves. They can be in any order you like, and you can have several such tables, with different ordering, even different sets of strings.
3. Code: Wait for a message string
Now to your actual code (the above is tables, not executable code). The first thing your code needs to do is wait until it knows there's a string the the Rx buffer, ready to be interpreted. In this case we know that a carriage return signals the end of an incoming string in the Rx buffer. Hence, we might wait until there is a carriage return present. Let's say this is a MultiTrack task, so we simply write a loop that waits for the carriage return, thus:
CRWait: YieldTask LoadX 13 ;Carriage return ComRx_FindXInBuf ;Is there a CR in the Rx buffer? GoIfXEQ 255,CRWait ;loop back if not
This code snippet will sit and loop until a CR appears. Then it will "fall through". Once it falls through we now for sure that there is a message waiting for us (barring something going wrong!).
4. Code: Find a string match.
This is where we use the actual ComRx_StrFind
instruction. First we have to make sure the NVEM access is correctly set up.
NVSetPtr FruitNames NVSetPage 0 ;0 is the default, so playing very safe! ComRx_StrFind ;Seek a match between Rx buffer and the tabulated strings
5. Code: Evaluate the result.
The ComRx_StrFind
returns its result in X, as follows:
- If a match is found, the X-register contains the number of the entry in the pointer table, counting from 1. Thus, if in this case the received string was
"Apple",13
, X would contain 3. - If a match is not found, X will contain 0 (which makes
Branch
instruction very appropriate).
If a match is found, the matching string is removed from the Rx buffer. If no match is found the Rx buffer is not changed.
Branch Target NoMatch Target RxOrange Target RxLemon Target RxApple Target RxPear Target RxBanana Target RxGrape RxOrange: ;Do the orange thing .... RxLemon: ;Do the lemon thing .... RxApple: ;Do the apple thing .... RxPear: ;Do the pear thing .... RxBanana: ;Do the banana thing .... RxGrape: ;Do the grape thing ....
You would place your own code to respond to each of the strings at RxOrange
, RxLemon
, etc. Quite likely you would then go back to CRWait
and wait for the next incoming message.
6. Flush any garbage.
This step is what may make the difference between something that works perfectly in the lab but falters in the field. It handles the case of a message being received garbled. Serial communications is not a perfect medium ... things can and do go wrong over any communications medium. Even if you believe you have a perfect transmission channel, something as simple as a cable being plugged halfway through a message will corrupt it. An electrical noise spike can corrupt a character. If your program can get hung up because it can't detect a partial message, it will have to be restarted.
The strategy to overcome this risk is that whenever there should be a valid message in the Rx buffer (in our case when there's a carriage return character), but ComRx_StrFind
is unable to decide what it is, we discard one single character from the head of the buffer. Remember, ComRx_StrFind
will only detect a message that starts at the head of the buffer. One single stray character at the very start will "blind" it. So we "shave" character off the head until we either expose a valid message or we have emptied the buffer. This strategy will ensure that any corrupted messages get discarded. Other strategies are possible, depending on what is known about a given application.
In the above code the Branch
target for no message is NoMatch
. So ....
NoMatch: ComRx_ReadOne ;Shave one character off the head GoTo CRWait ;Go back and try again
Putting it all together
To make it easier to understand, the description above started with the NVEM tables and ended with the executable code. In a real program all NVEM data must come last, and be preceded by a NVEM0
directive. If this is all new to you, please see NVEM Documentation. Here it all is, in the correct order (this is still just a skeleton, as you must make sure it gets invoked in the manner best suited to your program, and you must of course add the actions required for each detected type of fruit):
;Wait for a carriage return character to appear in the Rx buffer CRWait: YieldTask LoadX 13 ;Carriage Return ComRx_FindXInBuf ;Is there a CR in the Rx buffer? GoIfXEQ 255,CRWait ;loop back if not ;There's a CR in the buffer, hence a full message. Find a string match. NVSetPtr FruitNames NVSetPage 0 ;0 is the default, so playing very safe! ComRx_StrFind2 0 ;Seek a case insensitive match between Rx buffer and the tabulated strings ;Evaluate the result. Branch Target NoMatch Target RxOrange Target RxLemon Target RxApple Target RxPear Target RxBanana Target RxGrape ;Actions RxOrange: ;Do the orange thing .... RxLemon: ;Do the lemon thing .... RxApple: ;Do the apple thing .... RxPear: ;Do the pear thing .... RxBanana: ;Do the banana thing .... RxGrape: ;Do the grape thing .... ;Flush any garbage. NoMatch: ComRx_ReadOne ;Shave one character off the head GoTo CRWait ;Go back and try again ;=================================================================== NVEM0 ;Start of NonVolatile MEMory region 0 ;Messages/strings strOrange: NV0Byte 7,"Orange",13 strApple: NV0Byte 6,"Apple",13 strBanana: NV0Byte 7,"Banana",13 strLemon: NV0Byte 6,"Lemon",13 strPear: NV0Byte 5,"Pear",13 strGrape: NV0Byte 6,"Grape",13 ;Table of pointers FruitNames: NV0Ptr strOrange ;Entry #1 NV0Ptr strLemon ;Entry #2 NV0Ptr strApple ;Entry #3 NV0Ptr strPear ;Entry #4 NV0Ptr strBanana ;Entry #5 NV0Ptr strGrape ;Entry #6 NV0Byte 255,255 ;<<<<< Essential!!!
Notes/reminders:
- The instruction only removes from the buffer the characters of any string it matches.
- The string must be at the very head of the buffer.
- The instruction argument controls case sensitivity
- Each string in NVEM must be preceded by a count byte.
- The table of pointers must end in two 255 bytes.
- The record length and record number pointers are not involved in this instruction. Only the page and pointer matter.
Dialect exclusions: Not available in dialects before 29