aComRx_StrFind pp,c [D>=28]

Match a received string to a table in NVEM. This is subtly different to aComRx_StrFind2, which uses a different table format. The 'a' first letter of the keyword indicates that this is the "addressable" version, meaning the instruction can address a serial port other than the default.

This instruction seeks to match the data at the head of the serial Rx buffer for port pp with one or more "canned" strings stored in NVEM. Its primary use is for detecting received commands and machine generated ASCII messages. For example, we initially created it to assist in interacting with a cellular modem, for sending and receiving SMS 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 "NULL terminated" 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 terminated with a 0 byte. The 0 is not part of the string, it merely signals the end of the string (C-style NULL terminated strings).

strOrange:      NV0Byte   "Orange",13,0
strApple:       NV0Byte   "Apple",13,0
strBanana:      NV0Byte   "Banana",13,0
strLemon:       NV0Byte   "Lemon",13,0
strPear:        NV0Byte   "Pear",13,0
strGrape:       NV0Byte   "Grape",13,0
;        Terminator _________________^

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 aComRx_StrFind 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:

COM1            EQU               252        ;Address of the required serial port                
CRWait:         YieldTask
                LoadX             13         ;Carriage return 
                aComRx_FindXInBuf COM1       ;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!
                aComRx_StrFind    COM1,0      ;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 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:        aComRx_ReadOne	 COM1	      ;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
COM1            EQU               252        ;Address of the required serial port                
CRWait:         YieldTask
                LoadX             13         ;Carriage Return 
                aComRx_FindXInBuf COM1       ;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!
                aComRx_StrFind    COM1,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:        aComRx_ReadOne	COM1	 ;Shave one character off the head
                GoTo             CRWait ;Go back and try again 
;===================================================================
                NVEM0        ;Start of NonVolatile MEMory region 0
;Messages/strings
strOrange:      NV0Byte   "Orange",13,0
strApple:       NV0Byte   "Apple",13,0
strBanana:      NV0Byte   "Banana",13,0
strLemon:       NV0Byte   "Lemon",13,0
strPear:        NV0Byte   "Pear",13,0
strGrape:       NV0Byte   "Grape",13,0

;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:

  1. The instruction only removes from the buffer the characters of any string it matches.
  2. The string must be at the very head of the buffer.
  3. The instruction argument controls case sensitivity
  4. Each string in NVEM must end in a 0 (NULL) byte.
  5. The table of pointers must end in two 255 bytes.
  6. 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 28

Can only exist on boards that have an uncommitted serial port other than the programming port.