Shop OBEX P1 Docs P2 Docs Learn Events
SERIN parsing help needed. — Parallax Forums

SERIN parsing help needed.

odayoday Posts: 7
edited 2011-07-24 12:06 in BASIC Stamp
Hi,

I'm receiving a serial string (38.4k strict) using a 2p24 and able to take it into one variable. However, the string contains three comma-delimited data sets and are not fixed length, so I need to break the data out into three separate variables. I put together a decent example of what I have.

Anyone able to advise? I about broke my back spending the past couple days trying to solve this! I'm okay with parsing VAR raw instead of doing it during the SERIN, but I need help with that -- I don't know how. :(

Daniel

--

raw VAR BYTE ' No specific array is defined, but it reads the string in great!
vvv_aaa VAR BYTE(6) ' Max input string length is 5 chars
vvv_bbb VAR BYTE(6) ' Max input string length is 5 chars
vvv_ccc VAR BYTE(5) ' Max input string length is 4 chars


DO

PAUSE 1000
SEROUT RX, T38K4, ["go",CR] ' I have to use 38.4k. Data is coming from a 3rd party product.
SERIN TX, T38K4, 3000, NO_DATA, [STR raw\17\CR] ' This works great
DEBUG "RAW: ",STR raw,CR ' and returns "aaaaa,bbbbb,cc.c" or shorter perfectly
' should I go ahead and parse this variable into three separate
' ones rather than gamble on parsing during SERIN?

vvv_aaa(5) = 0 ' Defining byte 6 as "0" per Syntax Manual 2.2 Page 405
vvv_bbb(5) = 0 ' "
vvv_ccc(4) = 0 ' Defining byte 5 as "0" per Syntax Manual 2.2 Page 405
PAUSE 1000
SEROUT RX, T38K4, ["go",CR]
SERIN TX, T38K4, 3000, NO_DATA, [STR vvv_aaa\5\",",STR vvv_bbb\5\",",STR vvv_ccc\2\CR]

' vvv_ccc\2 is filled with garbage
' and sometimes vvv_bbb is garbage too, when real data is "--"
' vvv_ccc\4 (desired) hangs the program
' tried with and without \CR

DEBUG "RAW X 3: ", STR vvv_aaa, " ", STR vvv_bbb, " ", STR vvv_ccc, CR

LOOP

NO_DATA: ' Merely loops back in, prevents hanging, etc. Not important for troubleshooting the SERIN parsing.


///

DEBUG SAMPLE #1:
RAW: 2948,1592,0
RAW X 3: 2948 1592

Comments

  • stamptrolstamptrol Posts: 1,731
    edited 2011-07-19 05:09
    At that speed, just being able to reliably get the whole string is encouraging.

    By all means, if you must stay at 38.4K, do the parsing after receiving. The Stamps don't have comm buffers so if you slow down to parse on the fly, you'll miss characters.

    If you want to experiment, set up three SERIN statements with a WAIT modifier (see the Helpfile) so that you grab the first variable on the first data transmission, the second variable on the second data transmission and so on.
    It will take longer but may preserve some variable space. But, with the Scratchpad area available, its probably not a big deal.

    Cheers,
  • odayoday Posts: 7
    edited 2011-07-19 06:47
    Tom,

    I played around with the wait, like you sggest, but couldn't get it to work. I suspect an error on my part using wait.

    SERIN TX, T38K4, 3000, NO_DATA, [STR vvv_aaa\5\","] ' works
    PAUSE ... SEROUT
    SERIN TX, T38K4, 3000, NO_DATA, [WAIT(","), STR vvv_bbb\5\","] ' does not work
    PAUSE ... SEROUT
    SERIN TX, T38K4, 3000, NO_DATA, [WAIT(","), WAIT(","), STR vvv_ccc\4\CR] ' doesn't work either

    Can you offer and specific advice on how to parse var raw into separate variables, or help me properly use the WAIT modifier?

    Thanks,

    Daniel
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2011-07-19 09:37
    Daniel,

    You will definitely have to parse the string after receiving the raw data. The other parsing modifiers are simply too slow unless the other device can add extra stop bits pacing. If the amount of RAM buffer required is a problem, an alternative, also capable of relatively high speed reception on a BS2p, is the SPSTR modifier to receive the bytes directly into SPRAM.
    FOR idx=0 TO 18 : PUT idx, 0 : NEXT  ' zero the spram buffer
    SEROUT RX, T38K4, ["go",CR]                    ' I have to use 38.4k. Data is coming from a 3rd party product.
    SERIN TX, T38K4, 100, timeout, [SPSTR 17]    ' <<<< alternative using SPSTR
    timeout:  ' will have to come here via timeout if less than 17 chars received, okay.
    FOR idx=0 TO 18 : GET idx,char, : IF CHAR=0 THEN EXIT : DEBUG CHAR : NEXT
    
    There is some info on the timing of serial commands here.
  • stamptrolstamptrol Posts: 1,731
    edited 2011-07-19 09:50
    That's essentially what I had in mind.
    It may just be that at the high speed, it can't see 5 characters before the "," comes along. Try it without expecting the trailing "," You may find the fifth character in the string is the comma. Similarly with the CR for the third string. If the data is variable length and the specified number of bytes don't arrive, it will hang.

    You can also use the SKIP modifier which might act faster; but if you don't know how many characters to expect, it may not be too useful.

    With variable length, the only things you know for sure is two commas and a CR, so crunching everything after the capture may be the most effective.

    Then, a bit of a FOR-NEXT loop looking for the commas and CR will define how many characters in each number.

    Can you capture and post the data string from the data source with Hyperterminal so we can see what to expect?
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2011-07-19 10:33
    Here is an example of a parsing routine. It takes comma delimited ascii fields, from a record stored in SPRAM, and parses that into three word variables in RAM
    ' {$STAMP BS2p}
    ' {$PBASIC 2.5}
    wx VAR WORD
    wy VAR WORD
    wz VAR WORD
    char VAR BYTE
    idx VAR BYTE
    pntr VAR NIB
    
    ' main code would go here to receive the string into spram
    
    
    parse:  ' enter with string to parse in spram
      wx=0 : wy=0 : wz=0
      pntr=0     ' this will point in succession to wx, wy, wz
      FOR idx=0 TO 17
        GET idx,char
        SELECT char
          CASE 0, CR
            EXIT
          CASE "0" TO "9"
            wx(pntr) = wx(pntr)*10+char-48  ' accumulate digits into variable
          CASE ","
            pntr=pntr + 1   ' point to next word wy, wz.
         ENDSELECT
      NEXT
      RETURN  ' wx, wy wz contain values
    
  • odayoday Posts: 7
    edited 2011-07-19 21:51
    Thank you two so much for your help so far! So awesome! I feel now like I'm on the right track, but it's not working right. Weird. My debug output is below, and the code is below it. I like the direction you're going with this and definitely hope it works in the end!!

    -looks like the extended ascii output in the debug won't paste properly to the forum. Attaching a text file.

    Daniel

    ECK01 RAW: 1220,659,0
    TEST:
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2011-07-20 09:19
    The values in wx, wy and wz after parsing will be numbers, not strings, so you can use the DEC modifier instead of the STR modifier. Also a good idea at this stage would be to debug the raw ascii string captured by the SPSTR option. If it is not correct, you can fall back to the command that captures the string to ordinary RAM, which you already know works.

    ... snippet
      SERIN TX, T38K4, 3000, timeout, [SPSTR 17]    ' <<<< alternative using SPSTR
    timeout:  ' will have to come here via timeout if less than 17 chars received, okay.
       DEBUG CR, "--raw SPRAM: "
      FOR idx=0 TO 18  ' <--- this displays what is captured raw in the SPRAM
        GET idx,char
         IF (char>=32) AND (char<128) OR (char=CR) THEN
            DEBUG char   ' printable
         ELSE
            DEBUG "{", HEX2 char, "}"   ' possible non-printable as {HEX}
        ENDIF
       NEXT
       DEBUG CR
      GOSUB PARSE  ' <--- now condense the raw ascii to 3 numbers
    
       DEBUG "TEST: ", DEC wx, " ", DEC wy, " ", DEC wz, CR   ' <--- DEC not STR
    
  • odayoday Posts: 7
    edited 2011-07-20 22:02
    Tracy, Awesome! Working! Solved! Thank you!!

    A couple brief (fairly important) questions about it, if I may;

    1. The template for the third serin field is xx.x (or) x.x. Does the code you advised drop the decimal point, or what kind of behavior might happen?

    2. Where you used .., timeout, [..., I had previously been trying to use a subroutine that resets the sending device and goes back to the parent routine to try again. I understand the purpose of the timeout as used here, but wonder how I might go elsewhere if no data is received?

    Thanks again,

    Daniel
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2011-07-21 11:23
    Great!

    1) I believe based on the logic of the code that it just loops over the decimal point and returns a number in units of tenths. So xx.x will be xxx, and x.x will be xx. You should be able to print the result with the radix point using,
    DEBUG DEC wz/10, ".", DEC1 wz
    

    2) If no data at all is received, the very first character and the ones after it in the SPRAM buffer will remain as zeros. So, a simple solution would be to look and bail out if that first char is zero:
    timeout:  ' will have to come here via timeout if less than 17 chars received, okay.
      GET 0, char  ' test the first char to see if we received anything
      BRANCH char, do_over
       GOSUB PARSE
    
    A more sophisticated test would bail out if there are too few chars, or if unallowed chars are found, or if the data values are out of expected range. The need for that will depend on looking at the raw data string to see what sort of errors (if any!) crop up.
  • odayoday Posts: 7
    edited 2011-07-23 18:25
    Tracy,

    Amazing -- thank you! I had to alter it a bit, since I do consider "0" to be valid data. I did try your
    DEBUG DEC wz/10, ".", DEC1 wz

    but it seemed to result inaccurately. Specifically, whenever wz is "6", I got "0.6". I'll fiddle more with it.

    - Daniel


    In my main:

    FOR idx=0 TO 17 : PUT idx, "a" : NEXT
    SEROUT RX, T38K4, ["go",CR]
    SERIN TX, T38K4, 3000, GOT_DATA, [SPSTR 17]
    GOT_DATA:
    GET 0, char
    IF char = "a" THEN GOTO NO_DATA
    GOSUB PARSE
    RETURN

    And commented out part of the parsing routine that seemed to be interfering with the zeros:

    PARSE:
    wx=0 : wy=0 : wz=0
    pntr=0 ' this will point in succession to wx, wy, wz
    FOR idx=0 TO 17
    GET idx,char
    SELECT char
    ' CASE "0", CR
    ' EXIT
    CASE "0" TO "9"
    wx(pntr) = wx(pntr)*10+char-48 ' accumulate digits into variable
    CASE ","
    pntr=pntr + 1 ' point to next word wy, wz.
    ENDSELECT
    NEXT
    RETURN ' wx, wy wz contain values
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2011-07-24 12:06
    Hi Daniel,
    It has to be
    CASE 0, CR
    not
    CASE "0", CR.

    The former looks for a null, that is, the actual ascii code zero, whereas "0" looks for the ascii representation of zero, which is the ascii code $30 hex or 48 decimal.
    From what you have said, the data will all be printable ascii characters, and the null would not normally occur.
    That CASE is important because it gives the criterion for exiting the loop early, in case there is no data at all, or in case it has processed the entire received string.

    I'm not sure about the 6. So, this device can send back xx.x, x.x, or simply x? That will take a little more code.
    IF wz>9 THEN 
      DEBUG DEC wz/10, ".", DEC1 wz
    ELSE
      DEBUG DEC1 wz
    ENDIF
    
Sign In or Register to comment.