Shop OBEX P1 Docs P2 Docs Learn Events
Decoding serial input — Parallax Forums

Decoding serial input

DiverBobDiverBob Posts: 1,110
edited 2014-06-16 06:23 in Propeller 1
It's getting late and my mind has decided it doesn't want to play anymore! A search through the forum didn't help so I'll ask here.

I want to output a series of numbers using the Parallax Serial Terminal object. The series will consist of a "T" for a start indication, a comma, a number, comma, and another number ("T",n,nnn). I can type the series into the serial terminal but my attempts at decoding it in the prop are falling flat. Any similar examples available out there?

Thanks

Comments

  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-06-08 18:29
    DiverBob wrote: »
    It's getting late and my mind has decided it doesn't want to play anymore! A search through the forum didn't help so I'll ask here.

    I want to output a series of numbers using the Parallax Serial Terminal object. The series will consist of a "T" for a start indication, a comma, a number, comma, and another number ("T",n,nnn). I can type the series into the serial terminal but my attempts at decoding it in the prop are falling flat. Any similar examples available out there?

    Thanks

    Well that's straightforward in Forth but you can do something similar in Spin by simply sitting in a loop which tests for digits and shifts them into an accumulator ( acc := (acc*10) + (char-$30). So when you encounter a non-digit you simply terminate with the result as the number. So this will work with commas or end-of-line but the T should be passed in a variable so that the app knows to disregard the result and synch or whatever. Not quite sure why you need to say "T" with the quotes though, it seems very redundant. Personally I would just use a symbol like a colon immediately followed by the first number :n,nnn
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-06-08 19:00
    DiverBob,

    I'm a bit confused by your post. You say you want to output (from the Prop) data in the given format, but the title and subsequent comments seem to indicate an input (to the Prop) operation. Which is it?

    -Phil
  • DiverBobDiverBob Posts: 1,110
    edited 2014-06-09 03:19
    DiverBob,

    I'm a bit confused by your post. You say you want to output (from the Prop) data in the given format, but the title and subsequent comments seem to indicate an input (to the Prop) operation. Which is it?

    -Phil
    I told you my mind wasn't with it last night! What I meant was that I want to use the Parallax Serial Terminal object on the prop to recieve data from my computer. I want to input a command string such as T,1,90 followed by a return char. The commas are to delineate the individual values following the start character. That command string then needs to be interpreted by the prop. Eventually instead of me typing in the command string, another prop will be providing the input to the prop.

    Last night I couldn't figure out how to parse the input string into its different components. This morning I'm doing better and have a couple of ideas to test tonight but as long as I've asked the question it would be nice to see how others have solved this type of issue. Oh yes, the T is just a transmit character to indicate the start of the command sequence, I just typed quotes around it, seemed like a good idea at the time...
  • max72max72 Posts: 1,155
    edited 2014-06-09 06:26
    It' very similar to a NEMA string like the ones coming from a GPS, and there are many objects around.
    That would be my starting point..

    Massimo
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-06-09 08:39
    In C you can do something like this:
    #include <stdio.h>
    
    char buffer[100];
    
    int main(void)
    {
        int int1, int2;
        while (gets(buffer))
        {
            if (sscanf(buffer, "T,%d,%d", &int1, &int2) == 2)
                printf("int1 = %d, int2 = %d\n", int1, int2);
        }
        return 0;
    }
    
    I've tested this with GCC under Cygwin, and it correctly decodes a string with the format "T,#,#". It rejects any string that does not follow that exact format. I haven't tested this under PropGCC so I don't know if it works exactly the same.

    EDIT: I tested it with PropGCC, and it works correctly on the Prop also.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-06-09 09:20
    Do a forums search on "parser" -- there are a few examples in Spin that I've participated in.
  • DiverBobDiverBob Posts: 1,110
    edited 2014-06-11 18:48
    Thanks for all the help so far! I think I found the answer I've been looking for in an object written by Phil Pilgrim called regex, a regular expression parser that it appears he wrote for GPS decoding. I've modified his demo and I can get it to work when I specify the input string in the program but I can't enter the same string in the Parallax Serial Terminal and get an output. I believe the problem is the getstr command used to input the string from the terminal to the prop but I can't figure out what other choice to use that works in FullDuplexSerialPlus. I've been using the Parallax Serial Terminal object for all my terminal work, first time I've used this object for serial communications.

    Maybe someone else can see where I went wrong:The program as it stands works until I add in the commented out lines and then comment out the line teststr := string("$T,1,2,345")
    con
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq
      MS_001   = CLK_FREQ / 1_000
      
    con
    
      RX1 = 31
      TX1 = 30
      SDA = 29
      SCL = 28
    
      CS  =  27
      CLK =  26
      DIO =  9
    
    obj
      io            : "FullDuplexSerialPlus"
      re            : "regex"
    
    pub main
      Serialtest
    
    pub SerialTest | teststr, pattern, resaddr, rslt, i  
    'test inputing data from PST and echoing it back to the PST
    ' input string format: "$T,1,2,345"
      io.start(31, 30, 0, 9600)
      io.tx(16)
    '  repeat
    '    pauseMSec(1000)
    '    io.Str(String(pst#NL, "Enter command: "))
    '    io.getstr(teststr)
        teststr := string("$T,1,2,345")
        pattern := string("\$T\s*,($1[\d])\s*,($2[\d])\s*,($3[\d]+)\s*")
         
        io.str(string("String:", 13, 13))
        io.str(teststr)
        io.str(string(13, 13, "Pattern:", 13, 13))
        io.str(pattern)
         
        rslt := -cnt
        resaddr := re.match(teststr, pattern, re#NOALT)
        rslt += cnt
         
        io.str(string(13, 13, "Time: "))
        io.dec(rslt / 80_000)
        io.str(string(" ms."))
        io.str(string(13, 13, "Results:", 13))
         
        if (resaddr < 0)
          io.tx(13)
          io.str(string("Error #"))
          io.dec(-resaddr)
        elseif (resaddr == 0)
          io.tx(13)
          io.str(string("No match."))
        else
          repeat i from 0 to 9
            if (rslt := long[resaddr][i])
              io.tx(13)
              io.dec(i)
              io.str(string(": "))
              io.str(re.field(i))
         
          io.str(string(13, 13, "Remainder of string:", 13, 13))
          io.str(re.remainder)
    
    PUB pauseMSec(Duration)
    '' Pause execution in milliseconds.
    '' Duration = number of milliseconds to delay
      waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
    

    SerialParsingTest - Archive [Date 2014.06.11 Time 21.34].zip
  • kuronekokuroneko Posts: 3,623
    edited 2014-06-11 19:05
    The getstr method expects an address to a byte array (where a string can be stored), e.g. (untested)
    VAR
      [COLOR="#FF0000"]byte  buffer[128][/COLOR]
    
    PUB SerialTest | teststr, pattern, resaddr, rslt, i  
    'test inputing data from PST and echoing it back to the PST
    ' input string format: "$T,1,2,345"
      io.start(31, 30, 0, 9600)
      io.tx(16)
      repeat
        pauseMSec(1000)
        io.Str(String(pst#NL, "Enter command: "))
        io.getstr(teststr[COLOR="#FF0000"] := @buffer{0}[/COLOR])
        pattern := string("\$T\s*,($1[\d])\s*,($2[\d])\s*,($3[\d]+)\s*")
      ...
    
    Currently you'd pass a random stack value (uninitialised local variable).
  • rogersydrogersyd Posts: 223
    edited 2014-06-12 17:30
    DiverBob wrote: »
    Any similar examples available out there?

    Thanks

    Check out this book and the associated code:
    http://www.parallax.com/product/32316
    ftp://ftp.propeller-chip.com/

    There is a terminal project included there that contains some great token parser routine. Even without the book the code should be useful to you as it is well commented.
  • DiverBobDiverBob Posts: 1,110
    edited 2014-06-13 13:34
    kuroneko wrote: »
    The getstr method expects an address to a byte array (where a string can be stored), e.g. (untested)
    VAR
      [COLOR="#FF0000"]byte  buffer[128][/COLOR]
    
    PUB SerialTest | teststr, pattern, resaddr, rslt, i  
    'test inputing data from PST and echoing it back to the PST
    ' input string format: "$T,1,2,345"
      io.start(31, 30, 0, 9600)
      io.tx(16)
      repeat
        pauseMSec(1000)
        io.Str(String(pst#NL, "Enter command: "))
        io.getstr(teststr[COLOR="#FF0000"] := @buffer{0}[/COLOR])
        pattern := string("\$T\s*,($1[\d])\s*,($2[\d])\s*,($3[\d]+)\s*")
      ...
    
    Currently you'd pass a random stack value (uninitialised local variable).

    That worked! I went back and looked at the getstr() command in FullDuplex SerialPlus, don't know why I missed seeing that it needed a string pointer...

    Thanks for the help. Now to get back to work on using the information!
  • DiverBobDiverBob Posts: 1,110
    edited 2014-06-14 12:17
    Although the input string is being broken up correctly, I don't seem to be able to assign the values to another variable. I think I'm missing something basic here but after 3 hours of playing with it, I'm fresh out of ideas. Here is the code in question:
    con
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq
      MS_001   = CLK_FREQ / 1_000
      
    con
    
      RX1 = 31
      TX1 = 30
      SDA = 29
      SCL = 28
    
      CS  =  27
      CLK =  26
      DIO =  9
    
    var
      byte buffer[128]
      word leg, command, parameter
    
    obj
      io            : "FullDuplexSerialPlus"
      re            : "regex"
    
    pub main
      Serialtest
    
    pub SerialTest | teststr, pattern, resaddr, rslt, i  
    'test inputing data from PST and echoing it back to the PST
    ' input string format: "$T,1,2,345"
      io.start(31, 30, 0, 9600)
      io.tx(13)
      repeat
        pauseMSec(1000)
        io.Str(String("Enter command: "))
        io.getstr(teststr := @buffer{0})
    '    teststr := string("$,1,2,345")
        pattern := string("\$\s*,($1[\d])\s*,($2[\d])\s*,($3[\d]+)\s*")
         
        rslt := -cnt
        resaddr := re.match(teststr, pattern, re#NOALT)
        rslt += cnt
         
        if (resaddr < 0)
          io.tx(13)
          io.str(string("Error #"))
          io.dec(-resaddr)
        elseif (resaddr == 0)
          io.tx(13)
          io.str(string("No match."))
        else
          repeat i from 0 to 3
            if (rslt := long[resaddr][i])
              io.tx(13)
              io.dec(i)
              io.str(string(": "))
              io.str(re.field(i))
              case i
                1: leg                := re.field(i)
                2: command            := re.field(i)
                3: parameter          := re.field(i)
        io.tx(13)
        io.str(string("Leg: "))
        io.dec(leg)
        io.tx(13)
        io.str(string("Command: "))
        io.dec(command)
        io.tx(13)
        io.str(string("Parameter: "))
        io.dec(parameter)
        io.tx(13)
        io.tx(13)
        
         
    PUB pauseMSec(Duration)
    '' Pause execution in milliseconds.
    '' Duration = number of milliseconds to delay
      waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
    
    

    The problem is when I attempt to assign the values to the Leg, command, and parameter variables. Here is the output I'm getting when entering a string of $,1,2,345 in to the program:
    Enter command:
    0: $,1,2,345
    
    1: 1
    2: 2
    3: 345
    Leg: 3246
    Command: 3246
    Parameter: 3246
    
    

    Hopefully someone will see what mistake I'm making here.

    SerialParsingTest - Archive [Date 2014.06.14 Time 15.01].zip
  • John AbshierJohn Abshier Posts: 1,116
    edited 2014-06-14 15:39
    I don't have the re object to look at but I am guessing that re.field returns a string pointer which works fine in the io.str(re.field(i)). Later you assign the string pointer to a long in the case statement. What you want to do is to convert the string pointed to by re.field to a long (an integer).

    John Abshier
  • DiverBobDiverBob Posts: 1,110
    edited 2014-06-14 18:20
    I don't have the re object to look at but I am guessing that re.field returns a string pointer which works fine in the io.str(re.field(i)). Later you assign the string pointer to a long in the case statement. What you want to do is to convert the string pointed to by re.field to a long (an integer).

    John Abshier
    I archived the project in my last post, that has the re object object in it. I believe the output of re.field is a string and the variables are all bytes. You should be able to open the archive and run the code, it contains everything needed. I believe I've tried what you are talking about but didn't get a usable output. It appears that the output is stored in the resaddr array but I couldn't get a usable output there either.
  • John AbshierJohn Abshier Posts: 1,116
    edited 2014-06-14 19:28
    I am traveling, but will try to look at it later next week if no one else answers. For some reason I cannot download a zip file from the forum on my laptop. It wants to do something with attachment.php.

    John Abshier
  • Bill HenningBill Henning Posts: 6,445
    edited 2014-06-14 20:32
    Bob,

    See http://forums.parallax.com/showthread.php/122295-Converting-string-to-decimal?p=905112&viewfull=1#post905112

    That should do it for you :)

    You can find other relevant/useful links with the following google search:

    https://www.google.ca/#q=string+to+integer+in+spin+for+propeller

    Hope that helps!

    Bill
  • DiverBobDiverBob Posts: 1,110
    edited 2014-06-15 07:58
    Bob,

    See http://forums.parallax.com/showthread.php/122295-Converting-string-to-decimal?p=905112&viewfull=1#post905112

    That should do it for you :)

    You can find other relevant/useful links with the following google search:

    https://www.google.ca/#q=string+to+integer+in+spin+for+propeller

    Hope that helps!

    Bill
    That was the ticket! The Str2Dec routine is what I needed to convert the output correctly. Thanks for the help, now on to testing that input sequence to see if it will move the robot leg correctly.
  • Bill HenningBill Henning Posts: 6,445
    edited 2014-06-15 15:09
    You are welcome - glad to help :)
    DiverBob wrote: »
    That was the ticket! The Str2Dec routine is what I needed to convert the output correctly. Thanks for the help, now on to testing that input sequence to see if it will move the robot leg correctly.
  • msrobotsmsrobots Posts: 3,709
    edited 2014-06-15 17:10
    I am traveling, but will try to look at it later next week if no one else answers. For some reason I cannot download a zip file from the forum on my laptop. It wants to do something with attachment.php.

    John Abshier

    Some Forum glitch. You need to replace attachment.php by the correct filename to save.

    It is doing this for a while now. Filename always shows as attachment.php.

    Enjoy!

    Mike
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-06-15 20:58
    You're bringing in heavy artillery for this relatively simple problem. Just for fun, here is a simple parser. It just waits for the "$," combination, and then expects three numbers separated by a non-digit, and terminated by a non-digit. For example, "1,2,345 <cr>"
    [SIZE=1][FONT=courier new]CON
      _clkmode = xtal1 + pll16x                '
      _xinfreq = 5_000_000
    
      N_VALUES = 3
      LAST_INDEX = N_VALUES - 1
    
    VAR
      long idx, value[N_VALUES]
    
    OBJ
      fds : "fullDuplexSerial"
    
    PUB trial | char
      fds.start(31,30,0,9600)
      waitcnt(clkfreq/10+cnt)
    
      repeat   ' for each string entered
        repeat idx from 0 to LAST_INDEX   ' clear data array
          value[idx]~
        idx~                           ' zero array index
        fds.str(string(13,13,"waiting for data... "))
        repeat until fds.rx == "$"     ' expect a "$"
        if fds.rx <> ","           ' expect "," else restart
          next
        repeat             ' capture digits of each number
          repeat while (char := fds.rx) => $30 and char =<$39
            value[idx] := value[idx] * 10 + char - $30
        until idx++ == LAST_INDEX   
    
    
        fds.str(string(13,"the values are: "))              ' show what we got.
        repeat idx from 0 to LAST_INDEX
          fds.tx(13)
          fds.dec(idx)
          fds.tx(":")
          fds.dec(value[idx])
    [/FONT][/SIZE]
    
  • DiverBobDiverBob Posts: 1,110
    edited 2014-06-16 06:23
    Great routine! I know the parser I used was over kill but half the fun is the journey, right! I like the simplicity of your routine, I'll try it out later and see how it goes.
    That's the best thing about here, all the different ways to go about accomplishing the same goal. I learned a lot through this exercise and that was at least half the fun!
Sign In or Register to comment.