Shop OBEX P1 Docs P2 Docs Learn Events
Need a bit more help on Strings2 — Parallax Forums

Need a bit more help on Strings2

bdickensbdickens Posts: 110
edited 2010-10-14 11:16 in Propeller 1
I'm still not getting what I need and I've tried a whole bunch of combinations. Mike Green got me part of the way, but I'm still stuck and after a few hours of frustration, I'm reaching out. It's almost the same problem as last time.

I have a string that is coming in from outside the routine. InDataRec.
That string contains a comma delimited set of values. I need to break it up into the individual values. So INdataRec contains the string
"WD01,1,40385,5,360,90,27,0,0,10" and needs to be parsed so that
Var1 = "WD01"
Var2 = "1"
Var3 = "40385"
Var4 = "5"
etc

I'm using the STRINGS2 and NUMBERS library in addition to PST. For this example, I just "print" the value (fromAddr). My issue is that it seems to be modifying the variables in odd ways.

I'm struggling with when to use the address (@) vs the variable name and I am pretty sure thats the issue, but I have tried many combinations and some were worse than this. So I am pretty sure it's in there

code is attached.
«1

Comments

  • StefanL38StefanL38 Posts: 2,292
    edited 2010-08-07 17:32
    In SPIN you have only three types of variables

    Byte containing 8 bit
    word containing 16 bit
    long containing 32 bit

    strings are stored in arrays of BYTES

    each character is represented by an 8bit value

    so something like

    byte MyChar

    MyChar := "A"

    makes sense

    but something like MyChar := "ABC" makes no sense at all
    because the string "ABC" needs 3x8=24 bits
    which can't be stored in a byte

    For strings longer than one byte you use arrays of byte
    VAR
       byte MyString[4] 'four bytes always maxstring-length plus ONE
    
      MyString[0] := "A" 'attention arrayindex starts at 0
      MyString[1] := "B"
      MyString[2] := "C"
      MyString[3] := 0  'zero-termination of the string
    


    methods that work with strings need the RAM-adress where the bytearray begins

    This means if you want to use method str of FullDuplexSerial
    you would code
      serial.str(@MyString)
    

    then method str sends byte for byte until it encounters value zero
    (which terminates the string)

    so for your string it would look similar like this
    VAR
      byte INdataRec[42] 
      byte MyString1[5]
      byte MyString2
      byte MyString3[6]
      byte MyString4
    

    assigning the substrings
      MyString[0] := INdataRec[0]
      MyString[1] := INdataRec[1]
      MyString[2] := INdataRec[2]
      MyString[3] := INdataRec[3]
      MyString[4] := 0
    

    For parsing a string you would use a loop that increments the byteindex

    until INdataRec[index] == ","

    if you need a higher speed for assigning the characters of the string
    you can use the command bytemove

    best regards

    Stefan
  • bdickensbdickens Posts: 110
    edited 2010-08-07 18:58
    Appreciate the response. Parts of this I get, but I'm still not clear on how it's working. I was aware that strings are arrays of bytes, not sure I get all the implications but aware. I was hoping to use the STRINGS2 functions, but even coding this up manually, It doesn't work. My example below uses round braces but the code attached has the right ones.

    So presuming I define

    VAR
    byte InDataRec(70), tDataRec(70)
    byte PrseChar

    Pub TestTRans | jindx, idx, tlen, tval
    pst.start(115_200)
    InDataRec := String("WD01,1,40385,5,360,90,27,0,0,10")

    Now I'm presuming that this will produce an array that has 31 elements, in the form
    InDataRec(0) will be W
    InDataRec(1) will be D
    InDataRec(2) will be 0
    ...
    InDataRec(30) will be 0
    inDataRec(31) will be the terminator.

    But I am not wholly sure that's true. The string is delivered to me as a parameter. In any event, I can loop through the array.

    prseChar := ","
    jindx := 0
    Repeat idx from 0 to strsize(InDataRec)
    ->pst.Char(InDataRec(idx)) ' This should show the characters. But even in HEX it's not
    ->if InDataRec(idx) <> prseChar
    --->TdataRec(jindx):= InDataRec(idx)
    --->jindx++
    ->else
    --->TDataRec(Jindx)~
    --->pst.str(tDataRec)
    --->jindx := 0

    I get garbage from the other pst.str(tDatarec)

    This code is loaded in the String Parse Problem BigLoop v1.spin attached.

    Thanks.
  • T ChapT Chap Posts: 4,223
    edited 2010-08-07 20:45
    Hello bdickens. If you have the Prop manual, you can look on page 289 under "Parameters" and that may help you some. I would also suggest for the beginning stages to start out with a much smaller string to test with, and once you get a few bytes working, then you can easily expand from there.

    Parameters and Local Variables

    Parameters and local variables are all longs (four bytes). In fact, parameters are really just variables that are initialized to the corresponding values specified by the caller of the method. Local variables, however, are not initialized; they contain random data whenever the method is called.


    Passing a value by reference with the Symbol Address operator is commonly used when
    providing a string variable to a method. Since string variables are really just byte arrays, there is no way to pass them to a method by value; doing so would result in the method receiving only the first character. Even if a method does not need to modify a string, or other logical array, the array in question still needs to be passed by reference because there are multiple elements to be accessed.


    Also see page 438 under PUB:

    • LocalVar is a name for a local variable (optional). LocalVar must be globally unique,
    but other methods may also use the same symbol name. All local variables are of
    size long (four bytes) and are left uninitialized upon each call to the method.
    Methods can contain zero or more comma-delimited local variables.
  • T ChapT Chap Posts: 4,223
    edited 2010-08-07 21:04
    Also, is there a specific reason you are running the PLL at x2 instead of x16?

    CON
    _CLKMODE=XTAL1+ PLL2X 'The system clock spec
    _XINFREQ = 5_000_000


    Try this instead:

    CON
    _clkmode = xtal1 + pll16x ' use crystal x 16
    _xinfreq = 5_000_000
  • bdickensbdickens Posts: 110
    edited 2010-08-08 05:56
    no reason on the clock, but I don't think thats affecting my strings ! Just happened to be the base code that I started with.

    I also appreciated the parameters pointer. I had read that as well. But in my code example I eliminated any of the complexity on that. It doesn't seem to explain why the code I posted isn't working. I don't have a choice on the string coming in. But I am getting all of the parameters passed.

    So can someone please look at either sets of code and help me ? I don't need a whole lot of explanation but getting either sets of code to work once will do the job. I've gotten almost the entire rest of the code to work, just this one block is defeating me.



    Thanks
  • T ChapT Chap Posts: 4,223
    edited 2010-08-08 09:19
    bdickens wrote: »
    no reason on the clock, but I don't think thats affecting my strings ! Just happened to be the base code that I started with.

    I also appreciated the parameters pointer. I had read that as well. But in my code example I eliminated any of the complexity on that. It doesn't seem to explain why the code I posted isn't working. I don't have a choice on the string coming in. But I am getting all of the parameters passed.

    So can someone please look at either sets of code and help me ? I don't need a whole lot of explanation but getting either sets of code to work once will do the job. I've gotten almost the entire rest of the code to work, just this one block is defeating me.

    Thanks



    The only reason I mentioned the speed is in case at your baud 115,000 whether the communication can be achieved at that rate, maybe it can but that seems like a fast baud for a slow clock speed. The PST demos all use x16 so that's why I was curious.
    Template for Parallax Serial Terminal Test applications; use this to quickly get started with a Propeller chip
    running at 80 MHz and the Parallax Serial Terminal software (included with the Propeller Tool).
  • bdickensbdickens Posts: 110
    edited 2010-08-08 09:47
    Ahh. Ok. Now I get the context for that. It hasn't been an issue, but I went ahead and updated it. The main program was at 16, this snippet happened to be at 2.

    But my frustration on this string issue is reaching really annoying. Hoping someone can aim me at the right direction.

    Thanks
  • John AbshierJohn Abshier Posts: 1,116
    edited 2010-08-08 10:08
    Your first program uses Strings2. But you didn't attach it, so I have no clue what it is doing. If you want help, include all the code. You can use the file archive feature of the Prop Tool to package all objects into one zip file.

    John Abshier
  • John AbshierJohn Abshier Posts: 1,116
    edited 2010-08-08 10:13
    longmove(@tDataRec, @InDataRec,startlen)

    startlen is length of string in byte. longmove moves that many longs. Also since they are used as strings I sould make tDataRec and IndataRec byte arrays instead of long arrays.

    John Abshier
  • T ChapT Chap Posts: 4,223
    edited 2010-08-08 11:51
    Bdickens, I tried to work with your code earlier. In the past I have never worked with a string where I needed to parse it after creating the string like:

    variable := string("somestring")

    What I experienced is that I could not parse the string that was created this way. When I create a string it is always by some method like (oversimplified):

    variable[idx] := tempvar

    This way you populate the array one byte at a time. Then, you can use PST to display the values pst.dec(variable[idx]) and see all the values correctly. John has given you one method to populate the array.
  • T ChapT Chap Posts: 4,223
    edited 2010-08-08 13:22
    Finally got this to work:
    PUB parsestringmethod
        Repeat
          repeat idx from 0 to strsize(indatarec)
               pst.hex(byte[indatarec][idx], 2)
               waitcnt[10_000_000  + cnt]     'slow down the loop
    
    

    You access each byte in the string as shown above.

    If the methods are all in the same cog, you can do this:
    PUB Main
        pst.start(115_200)
        repeat
           indatarec := string("ABCDEFG")
           parsestringmethod
     
    
    PUB parsestringmethod
       Repeat
           repeat idx from 0 to strsize(indatarec)
               pst.hex(byte[indatarec][idx], 2)
               waitcnt[10_000_000  + cnt]     'slow down the loop
    
    

    If the caller is in one cog and the parse method is in another, then you send a pointer to the byte with a parameter using @
    PUB Main
        pst.start(115_200)
        repeat
           indatarec := string("ABCDEFG")
           parsestringmethod(@indatarec)
     
    
    PUB parsestringmethod(datastring)
       Repeat
           repeat idx from 0 to strsize(byte[datastring])
               pst.hex(byte[datastring][idx], 2)
               waitcnt[10_000_000  + cnt]     'slow down the loop
    
    
  • bdickensbdickens Posts: 110
    edited 2010-08-08 13:44
    John Abshier - I didn't post it because it is part of the Object Exchange but I'll update the package. Thanks for the tip. Didn't realize that pulled the packages in also. I've included that in String Parse Problem V1.zip

    But the second code sample doesn't need it. BIGLOOP.

    And I tried the BYTEMOVE a while ago. Won't seem to work either.

    T Chap - That is exactly what I tried to do in the BIGLOOP code, and I couldn't do it either. Something about the way the string is stored is not blatantly obvious. I've fixed some minor anoyances in BIGLoop but uncovered a disaster a bit later.

    Thanks
  • T ChapT Chap Posts: 4,223
    edited 2010-08-08 13:48
    Bdickens, the code I posted is working which means the string is fine and accessing the string in that manner does work, although I stripped out all your other parts. Maybe start with what I posted to get that much working, then add back in the other parts and see where the problem lies.
  • T ChapT Chap Posts: 4,223
    edited 2010-08-08 13:54
    On another note, maybe this is just my mac BST displaying your code strangely, but you have a lot of code that is parked hard left. In your software do you have indentions for all the code other than the PUB lines?


    BTW you don't use @ as you are doing if it is all in the same cog.


    Try this:
      InDataRec := String("ABCDE,F,GHIJ,K,LMN,OP")
      Repeat idx from 0 to strsize(InDataRec)
        if InDataRec[idx] <> prseChar
             pst.hex(byte[InDataRec][idx], 2)
    
  • bdickensbdickens Posts: 110
    edited 2010-08-08 14:09
    T Chap : Not sure what the clues were to that but that worked. I can make the rest work accordingly. I would eventually like to know why the STRINGS2 package failed here, but this will get me moving.

    Can't thank you enough. That was a big help.
  • StefanL38StefanL38 Posts: 2,292
    edited 2010-08-08 14:28
    If code something like
    InDataRec := String("ABCDE,F,GHIJ,K,LMN,OP")
    

    the variable InDataRec does NOT contain the string itself

    The variable InDataRec contains the RAM-Adress of the first byte
    where the string is stored as a bytesequence.

    best regards

    Stefan
  • T ChapT Chap Posts: 4,223
    edited 2010-08-08 15:00
    StefanL38 wrote: »
    If code something like
    InDataRec := String("ABCDE,F,GHIJ,K,LMN,OP")
    

    the variable InDataRec does NOT contain the string itself

    The variable InDataRec contains the RAM-Adress of the first byte
    where the string is stored as a bytesequence.

    best regards

    Stefan

    That's what it tested out as, so that in other words the VAR declaration could have been
    InDataRec and it works out identical to if it were InDataRec[20], and trying to affect the memory following InDataRec on the next variable declaration did not seem to affect the string content of InDataRec.

    VAR Byte InDataRec, otherbyte[20]

    InDataRec := string("asdfasdfasdf")
    otherbyte := 10

    The string content of InDataRec is still in tact somehow, so the byte memory doesn't seem to be in consecutive order as they normally would have been use a normal means of assigning values to the array. I assume at compile the string gets written to DAT by default.
  • anhingusanhingus Posts: 10
    edited 2010-08-09 09:20
    might i suggest you reconsider the nature of csv data? "strings" are not really relevant here.
    the idea is that you input data as a byte within a loop. when you hit the comma, you
    break the loop and do something with what you have received.

    the only variable needed is for the ascii character coming in. sort of like processing a list.

    you know the number of items in the list, each item separated by a comma. length of the item or data type does not matter since all you are taking in is ascii.

    outer loop - number of commas that make up a line of input [items in the list]
    inner loop - keep inputting bytes until you hit a comma
    process the item within the loop - like print a; or b=b+a
    close inner loop
    process item - feed into a variable, print it, build a string, use in function, etc.
    fetch the next line of data until there ain't no mo. close loop. use data.
  • bdickensbdickens Posts: 110
    edited 2010-08-10 13:13
    I understand your point, but as a rule, if I can find a function to do something, I'm happier with that. That is the point of the object exchange ! And while the loops code up easy enough, they are less obvious than a function call.

    But of course it has to work.... I still don't understand why the STRING2 object did not do what I expected.

    So the current generation of code does what you describe, but still does not work the way I'm expecting. But I'm getting closer.

    Thanks
  • StefanL38StefanL38 Posts: 2,292
    edited 2010-08-10 13:27
    from a quick look at the strings2-object.

    EVERY method of the strings2-object works with pointers.

    This means you have to define byte-arrays where the characters of the strings are stored and you have to define variables that are LONGs !!
    longs = 32 bit NOT bytes = 8bit which are used as pointers that contain the
    RAM-adresses of the byte-arrays.

    Whenever you code something like

    string("abc")
    somewhere in the RAM four bytes are reserved ("ABC+zero)
    and coding

    debug.str(string("abc")) is translated from the compiler to

    debug.str(RAM-adress where characters "ABC" are stored)

    do you get the principle by this desrciption?

    best regards

    Stefan
  • bdickensbdickens Posts: 110
    edited 2010-08-11 08:48
    Thank you. I think that finally sunk through to my thick skull. You obviously have a better grip on this than I do. So I do have some open questions. I notice when I define something like this.

    VAR
    byte InDataRec[MaxRec], tDataRec[MAXREC]
    byte PrseChar, inChar

    I could be wrong, but it looks like InDataRec and TDataRec both share the same "string" space. So a value put into InDataRec in byte 2 shows up in TdataRec as byte 1. So it looks like the compiler assigns one address to InDataRec and the next contiguous address to tDataRec. Then when I try to access them, I end up with colliding strings. All this is making it difficult to do what seems to be a simple job.

    So I'm up for some additional help.

    My original issue is that the system on the other end of this application sends a comma delimited stream of characters through an XBee, with no more than 10 variables but no less than 3. All are separated by commas. Other than the first 4 bytes, which are standard, the rest are variable length, all less than 60 bytes total. The XBee puts them in a string. All in a separate COG (eventually).

    The sending system is running behind, I'm running ahead. So my intent was to simulate the stream coming in. I defined a variable, loaded a comma delimited string into it and the fun started.

    This is the routine that took the string, and copied it into what will be a global variable. That variable is accessible by multiple cogs. This code seems simple enough but displays some interesting behaviour. T Chap cracked the "cast" statement so at least I see whats happening.

    My intent, was to define 10 variables. Loop through the string and everywhere i find a comma, go ahead and close the variable. Save the result and move on.

    I've attached 3 pieces of code.

    Solution 5 - The basic code for a string copy (thats not working). This was my attempt to simulate what I'm going to get from the XBEE
    Solution 4 - this is the same code with the Parallax serial terminal code for debugging
    Solution 2 - This is the first cut at the parse routine. If I get copy to work, then Parse should be a snap. But I included it for reference.

    I'll take all the help I can get. I'm amazed at how much I got done, but how one itty bitty issue has stumped me.

    Thanks
  • John AbshierJohn Abshier Posts: 1,116
    edited 2010-08-11 10:27
    The compiler assigns one address to InDataRec. The address of TDataRec is the address of InDataRec + MaxRec. Try this program
    CON
      MaxRec = 10
    
    VAR
       byte InDataRec[MaxRec], tDataRec[MAXREC] 
    
    PUB Main
      repeat 
    
    Hit F8, it uses 5 longs of variable space. Change MaxRec = 10 to 100 and hit F8. It now uses 50 longs of variable space.

    Here is a changed version of version 5
    {{
    }}
    
    OBJ
      pst  :"Parallax Serial Terminal"    
    CON                                                                   
      _CLKMODE=XTAL1+ PLL16X   'The system clock spec
      _XINFREQ      = 5_000_000
      MAXREC = 30
      DATLEN = 10
    VAR
      byte InDataRec[MaxRec], tDataRec[MAXREC] 
      byte PrseChar, inChar
    
    Pub Main | jindx, idx, tval, tString, vstring
    
    pst.start(115_200)
    waitcnt(clkfreq * 3 + cnt)
    pst.str(string("in main",13))
    Tstring := String("ABCDE,F,GHIJ,K,LMN,OP")
    prseChar := ","
    jindx := 0
    copystring(tstring)
    pst.str(string(13,"InDataRec = "))
    pst.str(@InDataRec)
    
    PUB Copystring(istring) | idx       '    , tval, tstring
      pst.str(string("in copystring",13))
      pst.dec(strsize(istring))
      pst.char(13)
      repeat idx from 0 to strsize(istring)
          inChar := (byte[istring][idx])
          if idx < strsize(istring)    ' don't print the terminating 0 of string since that clears pst.exe
            pst.char(inchar)
          InDataRec[idx] := inChar
          tDataRec[Idx] := inchar       
    

    John Abshier
  • bdickensbdickens Posts: 110
    edited 2010-08-11 10:53
    Wow. That was quick. I'm separated from my propeller for a day, but I'll try this as soon as I get back. I may have some follow up questions.

    Thanks
  • bdickensbdickens Posts: 110
    edited 2010-08-12 07:26
    I did promise follow ups .....

    Why pst.str(@InDataRec) after copystring and not just pst.str(InDataRec) ?

    Tstring is defined as Long, inChar and InDataRec as byte. The statement : inChar := (byte[istring][idx]) casts the long to the byte variable. If I wanted to create long variables (WS1, WS2, etc) , could I have eliminated the cast ? and just done WS1[JDX] := istring[Idx] ? (this assumes that JDX is on a different loop, reset by the comma)

    Appreciate the help. I'm getting there. Slowly, but getting there
  • John AbshierJohn Abshier Posts: 1,116
    edited 2010-08-12 07:46
    Pst.str needs the address of a string. pst.str(InDataRec) will use the first 4 bytes of the InDataRec array as an address. tstring is a long it holds an address not the string values. Have you read the Prop manual? The program I posted works. Go through it line by line until you understand what it is doing.

    John Abshier
  • bdickensbdickens Posts: 110
    edited 2010-08-12 09:28
    I AM going through it line by line. (And yes, I know it works or I wouldn't bother)
    Did I read the manual ? yes,
    Do I get it all. Nope. This is my first large program past the labs and judging from the number of "string" related questions on the forum. I'd have to say it's not wholly clear. But I think that why I am asking questions.

    Appreciate the help.
  • charleyshfcharleyshf Posts: 165
    edited 2010-10-12 05:59
    Good morning....

    Wow, just like bdickens I am working with a sensor (except mine is a blackfin camera that does image processing) that I WANT to use the prop to send / receive commands and process the information. I have spent the past few weeks going through my prop books, the pekit, and LOTS of forum searches. Just before I called it quits and fell off my chair I found this thread..

    I have no problems using the prop to send commands to my sensor, (115200 baud) the problems come into play when I want to debug the result to pst and also use the data. Lets say for example I send a command and the prop receive's the data in the format:

    ##vmean 111 222 333
    12345678912345678912

    result[20]

    and I need to take each group of 3 numbers(no higher than 255) and assign a value to each one, so there are 3 values
    value1[0] = result[9]
    value1[1] = result[10]
    value1[2] = result[11]
    value1[3] = 0 4th byte of each value being 0(terminator)

    etc,etc,etc

    a note on the sensor I use, most responces are returned then followed by using cr then lf.

    Looking at the code examples myself I am trying to understand this better myself. I attached the objects I am using.


    I didn't see anything I could use from the object exchange, and there aren't really any useful examples other than this thread.

    One thing that I am hoping to do is write an object for the Blackfin Camera and post it in the obex for others to make use of.
  • T ChapT Chap Posts: 4,223
    edited 2010-10-12 07:51
    Charlie, you are trying to print the hex value of second number of the y_mean it looks like. What is the number you are seeing? You should see the hex value of ascii '1' which is 31.

    I don't use PST, but assume that you are sure that it requires the byte[y_mean][x] to access your local variable and not @y_mean. In all your earlier calls to print from PST, you use @ plus yo9ur local variable, but then for the number you are getting wrong, you are not using @ and are using byte.
  • charleyshfcharleyshf Posts: 165
    edited 2010-10-12 11:40
    Hello,

    Thank you for getting back to me..

    I think you are talking about this part of my code?
    Y_Mean[0] := BFinMean[8]
    Y_Mean[1] := BFinMean[9]
    Y_Mean[2] := BFinMean[10]
    Y_Mean[3] := 0

    I made changes so now it looks like:
    Y_Mean[0] := @BFinMean[8]
    Y_Mean[1] := @BFinMean[9]
    Y_Mean[2] := @BFinMean[10]
    Y_Mean[3] := 0

    If this is correct, I just want to be able to output via pst the value of Y_Mean which 'should' match the first 3 numbers, just trying to get that part of it right but I am still missing something here...



    T Chap wrote: »
    Charlie, you are trying to print the hex value of second number of the y_mean it looks like. What is the number you are seeing? You should see the hex value of ascii '1' which is 31.

    I don't use PST, but assume that you are sure that it requires the byte[y_mean][x] to access your local variable and not @y_mean. In all your earlier calls to print from PST, you use @ plus yo9ur local variable, but then for the number you are getting wrong, you are not using @ and are using byte.
  • T ChapT Chap Posts: 4,223
    edited 2010-10-12 11:54
    When all the variables are local, you don't need to use @ to assign one to the other as you just did. What you had was correct. But, you are not using the pointer to send the value to pst.

    Do this and post the result:

    PST.dec (Y_Mean[0])
    PST.dec (Y_Mean[1])
    PST.dec (Y_Mean[2])
    PST.Str (String(13))

    You should see:

    111

    Also if that works, then try this and post the result:

    Create a new Long called MeanResult[3]


    MeanResult[0] := Y_Mean[0] * 100
    MeanResult[1] := Y_Mean[1] * 10
    MeanResult[2] := MeanResult[0] + MeanResult[1] + Y_Mean[0]
    PST.dec (MeanResult[2])
    PST.Str (String(13))

    EDIT: this did not account for a byte possibly exceeding a byte maximum 255. See corrected code above.
Sign In or Register to comment.