Shop OBEX P1 Docs P2 Docs Learn Events
Could use some suggestions on delimiting/splitting strings — Parallax Forums

Could use some suggestions on delimiting/splitting strings

T ChapT Chap Posts: 4,198
edited 2006-11-02 06:09 in Propeller 1
I have an app made with Realbasic that will send some strings to a motor controller(Prop). The 4 sliders are set by the user on th ui, and a Run button in the app will assemble the 4 individual strings into one delimited string and write it to the usb2serial. I am looking for a good example of a method to read it in to the Propeller and split the string based on the delimiter. I probably will use the fullduplex serial object. Hopefully there exists some example somewhere for the idea, but so far haven't seen exactly what is needed for a guide.

Here is the concept thinking out loud:

1. read the string into array()
2. break up the array into the 4 parts x,y,z,a
convert those parts to decimal value long
3. since the string is a position, the code will note where the motor is, and send the difference in pulses with Dir to the right motor. The will be ramping and decel to deal with.
4. Send back the string to the app for error checking(wireless serial).

Should there be a header as well as delimiters between the strings and some tail to say it is finished? The string values range from 0 to 100,000+. Thanks for any suggestion on a reliable method of doing this.


An additional feature of the UI could be to manage some variables in the Propeller. Instead of the user needing the source code and Propeller tool, the Realbasic application could send a string to instruct the variable to be updated and written to a new value correct?

i.e. A parameter that needs to be set by the use called X
The UI sends a string updateX10 and the Prop takes the string, identifies it as the X variable to update, changes X to 10.

This is reasonable right?

Post Edited (originator99) : 10/31/2006 7:11:21 PM GMT

Comments

  • T ChapT Chap Posts: 4,198
    edited 2006-11-01 12:12
    If any one can offer a suggestion for the following I'd really appreciate it.

    There is a app that will send a string from the computer to the Propeller. Here is the general idea from the app, leaving out a number of extra parts that will be included:
      dim ts as TextOutputStream
      X =Xslider.Value
      Y =Yslider.Value
      Z =Zslider.Value
      R =Rotslider.Value
      OutputString = str(X)  + TAB + str(Y) + TAB +  str(Z) + TAB + str(R)  + CR
      Serial1.Write outputString
    
    



    On a button press the string is sent to the usb2serial. The values for x,y,z, and r can range from 0 to 100,000+. Sent as the string, they are simply ascii text, not real numbers.

    I can read the string into the Propeller easily, but dealing with it once its there is not clear. I need a way to take the string, separate the parts, convert the ascii strings to real numbers, whether binary or integers, whatever the protocol is, so that the represented ascii value can be assigned into it's own variables like:

    X = 100000 and a pin can actually pulse that many times based on a pause inbetween pulses.

    Thanks



    VAR
        byte recbyte[noparse][[/noparse]16]
        byte array[noparse][[/noparse]16] 
    obj
     ser : "fullduplexserial"
     term    : "tv_terminal" 
    
    PUB Start | i
     term.start(12)
     dira := %11111111_11111111_11111111_11111110
     outa := %00000000_00000000_00000000_00000000
     ser.start(0, 1, 0, 9600)      'rec tx mode baud
     array := 0
     repeat
      getrx 
       term.str(@recbyte[noparse][[/noparse]0])       
    PRI getrx
       recbyte := ser.rx
    PRI receiveStr(address,count) | c
      repeat count                      'start counting
       c := ser.rx                      'c = first rec'd byte
       if c == $0D                      'if c is CR,
          byte[noparse][[/noparse]address] := 0            'this element = 0  
          return                        'now return
       byte[noparse][[/noparse]address++] := c             'update c
    
    
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-01 14:30
    For a start, I'd change your receiveStr routine to end on either a tab or return like this:
    PRI receiveStr(address,count) | c
      repeat count                      'start counting
        c := ser.rx                      'c = first rec'd byte
        if c == $0D or c == $09      'if c is CR or TAB,
          byte[noparse][[/noparse]address] := 0            'this element = 0  
          return c                        'now return delimiter for optional checking
        byte[noparse][[/noparse]address++] := c             'update c
      return 0                         'default delimiter is zero
    PRI convertStr(address) | c
      repeat while c := byte[noparse][[/noparse]address++]
        result := result * 10 + (c = "0")
    
    


    Now, for each numeric string, you do:
    if receiveStr(@array,16) <> $09 ' or $0D as needed
      abort
    X := convertStr(@array) ' This works for ASCII to 32 bit binary
    
    


    There are not currently routines to convert ASCII to floating point.
    This will work for integers if you do an FFloat to convert to floating point.
    If you need a generalized string to floating point routine, you'll have to
    write it yourself or get someone else to do it. Note that "convertStr"
    doesn't do any error checking.

    Post Edited (Mike Green) : 11/1/2006 9:04:07 PM GMT
  • T ChapT Chap Posts: 4,198
    edited 2006-11-01 19:44
    Thanks Mike I am trying to make sense of the new version. I have included the updates guessing at what you intended. What I really need is to get it clear what is going on, here is my take on it:

    1. The ser.rx sets the FullDuplex object ready to receive whatever comes in. I am guessing that the object stores the entire string that shows up in its buffer at once? The string that I am sending it is the following:

    50000 + TAB + 50000 + TAB + 5000 + TAB + 1000 + CR

    If this math correct, then I am sending 22 bytes. I believe the Full Dup object is a 16 byte buffer, probably irrelevant as I have seen earlier versions of this store and display the entire string as sent, so that makes me think that the buffer size is not a limiting factor here, otherwise it would be chopped. Since it was all visible, I am guessing that the caller is reading on every iteration of the objects readbyte, and that the caller is responsible for managing the ultimate size of the string. Obvisouly there is great confusion here!

    2. The caller reads one byte at a time until it sees a specified delimiter, then what? In the code posted below, using the same numbers above for string, term.str(@array) displays "5" only, then says " updated address" to indicate it just updated 1, but then when it hits the

    if receiveStr(@array,22) <> TAB it does abort, and shuts down everyting.

    What it looks like is that the first byte received is correctly displayed (5 of the 50000), but the next byte is really a 0 from the second byte of the string, so it aborts. I am sure that I just imlpemented your ideas erroneously.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-01 21:04
    My apologies. The indenting was off on the "return 0" and I didn't notice when I posted it. I re-edited my sample code and re-posted it. Please look again at it. I'll look more at your test code later today.
    Mike
  • T ChapT Chap Posts: 4,198
    edited 2006-11-01 21:57
    Here is the updated version that is much closer.
    In debug on term,
    -the first string appears correct
    -the second vaue Y is skipped
    -the third value Z is correct
    -then nothing else and it is done no matter what you send

    
     repeat
      receiveStr(@array, 22)                                       
       term.str(@array)
       term.out(CR)                                    
       if receiveStr(@array,22) <> TAB ' or CR as needed                                 
         abort
    
    PRI receiveStr(address,count) | c
      repeat count                      'start counting
       c := ser.rx                      'c = first rec'd byte   
       if c == TAB or c == CR      'c == CR or TAB,                              
          byte[noparse][[/noparse]address] := 0 
          return c                        'now return delimiter for optional checking     
       byte[noparse][[/noparse]address++] := c             'update c                     
      return 0                                                      
                              'default delimiter is zero
    PRI convertStr(address) | c
      repeat while c := byte[noparse][[/noparse]address++]
        result := result * 10 + (c := "0")
    PRI getrx
       recbyte := ser.rx
    
    
  • T ChapT Chap Posts: 4,198
    edited 2006-11-01 23:25
    Mike, I removed the

    if receiveStr(@array,22) <> TAB ' or CR as needed
    abort

    and it returns each string individually on separate lines now in debug, it stops after the last string is received which is great. Any new string received is shown as well, so now to get the conversion toi XYZR going and it will be very close.

    Post Edited (originator99) : 11/2/2006 12:18:12 AM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-02 01:28
    Try this. I named the numbers X,Y,Z,R. This reads each numeric value and its delimiter, then displays the values after all 4 have been read. Note that the DIRA and OUTA registers are initially zeroed by the hardware, then the display and serial routines initialize their bits as needed. You don't have to do it. Note that term.dec expects a numeric value, not an address. I hope the rest is self explanatory. Let me know if you need floating point input. It's easy to do a fixed point value (without an exponent, but with an optional decimal point).

    Mike
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-02 01:37
    Oops. Change convertStr as follows:
    PRI convertStr(address) | c
      repeat while c := byte[noparse][[/noparse]address++]
        result := result * 10 + (c - "0")
    
    
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-02 01:56
    Here's an untested modified version for floating point. The FloatMath and FloatString objects are the ones included with the Propeller Tool. Look at both objects for more information on what you can do with them.
  • T ChapT Chap Posts: 4,198
    edited 2006-11-02 01:56
    Since it is for step pulses it doesn't need to float. The values coming into it are all integers(as strings), all positive.


    Ok just first gland this is really cool. With no changes to your code here is the outut to tv

    "0" + TAB + "0" + TAB + "0" + TAB + "0" + CR

    =

    48, 48, 48, 48 which are ascii equivs to 0, so even in term.dec it is reflecting ascii number values?

    Then, sending these values in place of "0"

    50000 = 533328 X
    50000 = 533328 Y
    5000 = 53328 Z
    1000 = 53328 R

    Sort of strange that sending 1k or 5k produces same answer. When you say numveric value,are you referring to it's integer relationship and not ascii number? If the number are stuck in ascii values what method is ther eto convert to either binary or decimal to use in output?




    This is a great thing, thanks!
  • T ChapT Chap Posts: 4,198
    edited 2006-11-02 02:00
    AHA! There you go, excellent, I though you were referring to decimal points required, my miscommunication, this is it!
  • T ChapT Chap Posts: 4,198
    edited 2006-11-02 02:47
    Mike if you want to use X as a number like this from the Start method:

    PRI moveX
    outa := 0
    dira := 1
    repeat from 0 to X '( or sub fltstr.floatToString(X) for X)
    outa := 1
    waitcnt(800_000 + cnt)
    outa := 0
    waitcnt(800_000 + cnt)

    Should this be doable? I get infinite pulses on the pin.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-02 02:57
    I can't tell from the indenting what's what (use code brackets)
    PRI moveX
      outa[noparse][[/noparse]0] := 0
      dira[noparse][[/noparse]0] := 1
      repeat X
        outa[noparse][[/noparse]0] := 1
        waitcnt(800_000 + cnt)
        outa[noparse][[/noparse]0] := 0
        waitcnt(800_000 + cnt)
    
    


    This should give you pulses 0.01 sec low, then 0.01 sec high, etc. If you really wrote "repeat from 0 to X" and it compiled, that's a compiler error. There must be a variable name between the "repeat" and the "from".
  • T ChapT Chap Posts: 4,198
    edited 2006-11-02 03:19
    Sorry I forgot the brackets. I meant repeat i from 0 to x, does infinite loop on the pin. Repeat X also, infinite. Maybe you will see the obvious problem.

    Actually for some reason X tests positive for being > 65k when I send string value of 50, even though the tv shows 50 as well. I need to study those math and string objects.

    Post Edited (originator99) : 11/2/2006 3:33:28 AM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-02 04:28
    See the comments marked with *****. You have to remember that SPIN doesn't know anything about what's contained in a variable. It treats everything as 32-bit integers.
    Mike
  • T ChapT Chap Posts: 4,198
    edited 2006-11-02 04:44
    Actually was just on the page in the manual reading about z-strings. I moved the moveX to below the FRound conversion and it worked! I output "5" and get 5 pulses now.

    Around 10 days at 12-14 hours of work on the Real Basic app and Propeller code to finally see this work. You have saved the day again Mike.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-02 05:15
    By the way. If you don't need the floating point, you can strip it out and use the integer convertStr that I posted earlier:
    PRI convertStr(address) | c
      repeat while c := byte[noparse][[/noparse]address++]
        result := result * 10 + (c - "0")
    
    
  • T ChapT Chap Posts: 4,198
    edited 2006-11-02 06:09
    Well unless it is affecting performance in some way, maybe just leave it as it is nice for TV debug number. If I need the cogs maybe it will have to be taken out later.
Sign In or Register to comment.