Test 3: Retrieve multiple values from the server
This test shows how something entered on a web page can be sent to a SPLat and actioned. This is the basis of over-the-web remote machine control.
On the sandbox page there is an area at the bottom where you can enter two numerical values and two on/off settings. We will now fetch those from the server. The numeric values will be displayed on the LCD, while the two on/off settings will control front panel LEDs. (Remember, when you change any of these on the sandbox page you must click “Save” for the changes to become active).
The initial stuff
The codes starts of with the familiar actions, ending in sending the query string “config” (the query string doesn’t have to be name-value pairs!). I’m not showing that code here. At the end of it the server’s response will be in the SX10509 network Rx buffer. This consists of four name-value pairs separated by carriage return ('0D) characters.
Example:
therm=67.78 cktime=99 pump=0 valve=1
Initializing the parsing loop
The data that comes back from the server has to be parsed, meaning it must have its 4 component parts (the 4 variables) extracted. We will do this in a program loop, which works like this:
- Initialize the loop;
- Read one “chunk” from the SX10509;
- If there was nothing left in the SX10509, go to step 7;
- Determine which data item name is at the start of what we just read from the SX10509;
- Extract its value and act on it;
- Go to step 2;
- Finished.
Step 1, initializing the loop, consists simply of setting a variable, bSCH_RxBufferOffset, to 0. For each iteration of the loop bSCH_RxBufferOffset will be used to move the point where we read from in the SX10509 network receive buffer. Its value for each iteration of the parsing loop will actually equal the number of characters that were “consumed” on the previous iteration. Here’s the initialization code, which also clears the LCD:
OBLCD_Cls
SetMem bSCH_RxBufferOffset,0 ;Initialise the offset pointer
Each time we read from the SX10509 we will send it the current value of bSCH_RxBufferOffset. The SX10509 will add that to the previously accumulated read offset to decide where to start reading from. If we send it 0, the SX10509 resets its offset to 0.
The parsing loop
The basic principle of how we handle the returned data is this: Each data item identifies itself with a variable name and an equals sign. So we start at the beginning and test if the stuff at the start of the SX10509 network Rx buffer is a recognizable name. Assuming it is, we extract the value (up to the terminating carriage return or end of the data) and take whatever action is required for that named item. Then we move the starting point in the SX10509 network Rx buffer to skip the characters just processed. We repeat this (loop) until all the data in the SX10509 network Rx buffer has been used up. You should note that we do a fresh read of the SX10509 network Rx buffer into the Xwire Rx data block for each data item, starting from the accumulated offset in the SX10509 network Rx buffer.

The diagram above shows the situation after the first Read command is echoed by the SX10509. The yellow boxes are the 30 bytes that contain data in the Xwire Rx data block in the SPLat. The white on blue text is the contents of the Network Rx buffer in the SX10509 board. The arrow shows where data is moved from. Carriage return characters are represented by pipe symbols |. To the left you can see the read offset that’s used in the SX10509 for this read, and the number of bytes reported in bRxLength.
Here’s the code that actually reads from the SX10509. It uses the SCH_Read subroutine we developed earlier, with bSCH_RxBufferOffset as an argument, to read from the SX10509. When the read is done it checks that there was something there and terminates the process if there was nothing.
ParseLoop:
Recall bSCH_RxBufferOffset
XtoI
GoSub SCH_Read ;Read from SCH n/w Rx buffer at offset I.
;Result in Xwire n/w Rx data block
WaitForST sSCH_GotEcho ;Wait for the command to be echoed
GoIfMZ bRxLength,T3_NoMoreData ;Make sure SCH returned something, else quit
After the Read the program tests for a valid variable name at the start of the Xwire Rx data block. It does this using the iiStrFind instruction. iiStrFind was designed specifically for this kind of use. Its documentation explains in detail how it works. This section of code ends in a Branch to the appropriate code for each expected variable name, plus a catch-all if no match is found.
NVSetPtr ptrValueNames ;Set the NVEM pointer to the table of string pointers
NVSetPage 0 ;Playing safe
LoadI 0 ;Search from the 1st data byte in the Xwire network Rx data block
iiStrFind abRxData,kRxDataLen,0 ;Try to find a match in NVEM table of variable names
Branch
Target T3_NoMoreData ;Not found in the table
Target T3_Therm
Target T3_CkTime
Target T3_Pump
Target T3_Valve
After the first Read, when the SPLat program checks if there is a recognizable variable name at the start of the Xwire Rx data block, it will find therm=. At the same time I will have been incremented so it “points” to the start of the value (courtesy of the magic of iiStrFind). The program then does a Branch to T3_Therm, the code required to handle the variable therm. This extracts the value (67.78) using a iifGetNum. iifGetNum, increments I by the number of characters processed in extracting the value.
You will notice that iiStrFind and iifGetNum, have both incremented I by the number of characters each “consumed”. As we set I to 0 just before the iiStrFind, I will now contain the exact count of the total number of characters consumed in extracting the name-value pair.
A simple subroutine, UpdatePointers, not shown here, takes care of incrementing I to account for the carriage return character after each name-value pair and updating bSCH_RxBufferOffset ready for next time.
T3_Therm: ;Get the temperature value and display
iifGetNum abRxData,kRxDataLen,255 ;Get a float from the Xwire n/w Rx data block
OBLCD_SetCur 0,0
OBLCD_Text "T="
OBLCD_fDispW 5,2
GoSub UpdatePointers
GoTo ParseLoop
By the time therm has been processed bSCH_RxBufferOffset will contain 12, the exact number of characters in therm=67.78|.
After the second pass through the parsing loop, the situation is as shown below:

The second Read is performed with bSCH_RxBufferOffset=12, and the Read offset in the SX10509 also at 12. Hence the readout starts at the 13th character in the SX10509, which winds up in the 1st data byte of the Xwire Rx data block in the SPLat. This time, the reported byte count is only 24, as that is the number of characters actually transferred. This time the variable name cktime is extracted, along with its value, 99 (code not shown here). The number of bytes consumed is 10.

The third Read is performed with bSCH_RxBufferOffset=10. This is sent to the SX10509, where it is added to the already existing value of 12, to give a new offset of 22. Hence the readout starts at the 23rd character in the SX10509, which winds up in the 1st data byte of the Xwire Rx data block in the SPLat. This time, the reported byte count is 14 The variable name pump is extracted, along with its value, 0. The number of bytes consumed is 7. The code that handles pump updates a front panel LED. To do this it exploits the fact that ASCII 0 has bit 0 clear, while ASCII 1 has bit 0 set. Hence an AndM with 1 will give 1 or zero, which can be sent to the LED output.
T3_Pump:
iRecall abRxData ;'30 for OFF, '31 for ON
IncI ;Past the character just used
LoadX 1
AndM
Output oLED0
GoSub UpdatePointers
GoTo ParseLoop

The 4th read provides a bit of a change, because it has no terminating carriage return. The SX10509 will actually return '00 bytes on a read, if there is nothing else there. The absence of a carriage return is not a problem.

The fifth read returns nothing, because it is indexed past the end of the data. This is signalled in the bRxLength byte. You may recall that we test for this very condition near the top of the parsing loop.
GoIfMZ bRxLength,T3_NoMoreData ;Make sure SCH returned something