Shop OBEX P1 Docs P2 Docs Learn Events
If loop — Parallax Forums

If loop

In the snippet, when I type in "ledoff", the led goes off. Then I type in "ledon" to turn the led on, nothing happens. It seems like the elseif is not being invoked. I tried using the case method, I had no success. I am not sure what I am doing wrong with the if / elseif loop. Need some help.

Thanks
Ray

    repeat
        term.fstr0(string("What do you need."))
        get_str(BUF_SIZE-2)
        term.fstr1(string("%s"),@buffer)
        term.fstr0(string("\r"))

        if (buffer := "ledoff")
           pinl(38)
        elseif (buffer := "ledon")
           pinh(38)

Comments

  • JonnyMacJonnyMac Posts: 9,719
    edited 2026-03-07 19:57

    You're not showing the code for get_str() -- you should allow others to be able to run your code.

    A few errors:
    1. The := operator is for assignments; the == operator is for testing equality
    2. There is no string type in Spin so you cannot use ==, anyway.
    3. You need @ to point to a string buffer

    Spin has as built-in function called strcomp() that lets us check for matching strings. Give this a try.

      repeat
        term.fstr0(string("What do you need."))
        get_str(BUF_SIZE-2)
        term.fstr1(string("%s"),@buffer)
        term.fstr0(string("\r"))
    
        if (strcomp(@buffer, @"ledoff"))
          pinl(38)
        elseif (strcomp(@buffer, @"ledoff"))
          pinh(38)
    

    TIP: If you use an IO pin in more than one place you should give it a name so that editing your programs will be easier. I know you're just getting started, but this good habit will save you a lot of trouble later.

  • RsadeikaRsadeika Posts: 3,896

    OK, that did the trick. Since I plan to add other choices, would using case be a better option. I would need an example of how I would implement that in my code.

    In this program I used get_str(), how would that be used if the code were placed in the jm_fullduplexserial object.

    I am thinking that for other people just getting started maybe this program would be beneficial.

    '' test_term.spin2
    ''
    
    CON
        CLK_FREQ = 200_000_000
        MS_001 = CLK_FREQ / 1_000
        US_001 = CLK_FREQ / 1_000_000
    
        _clkfreq = CLK_FREQ
    
    
    con {terminal}
    
        BR_TERM = 115_200
        BUF_SIZE = 32
    
    
    
    obj
    
        term : "jm_fullduplexserial"
    
    var
    
        byte buffer[BUF_SIZE]
    
    
    PUB main()
    
       setup()
       term.rxflush()
       term.rx()
    
       term.tx(term.CLS)
       ''waitms(1000)
    
    
        term.fstr0(string("Enter your name: "))
        get_str(BUF_SIZE-2)
        term.fstr1(string("%s"),@buffer)
        term.fstr1(string("\r\rHello,%s,good luck\r"),@buffer)
        waitms(500)
    
        repeat
            term.fstr0(string("What do you need."))
            get_str(BUF_SIZE-2)
            term.fstr1(string("%s"),@buffer)
            term.fstr0(string("\r"))
    
            if (strcomp(@buffer, @"ledoff"))
               pinl(38)
            elseif (strcomp(@buffer, @"ledon"))
               pinh(38)
    
    
       ''repeat
        ''waitct(0)
    
    
       repeat
    
    
    pub setup()
        term.tstart(BR_TERM)
    
    pub get_str(maxlen) : len | k
        bytefill(@buffer, 0, BUF_SIZE)
        term.rxflush()
        repeat
            k := term.rx()
            case k
                31..127 :
                    if(len < maxlen)
                        buffer[len++] := k
                term.BKSP :
                        if (len > 0)
                            buffer[--len] := 0
             term.CR :
                buffer[len] := 0
                return
    
    
    
    
    
  • JonnyMacJonnyMac Posts: 9,719
    edited 2026-03-08 17:39

    I am thinking that for other people just getting started maybe this program would be beneficial.

    In my opinion, string processing is not a beginner topic with the Propeller, but if want to do it I'll share my experience (as I'm sure others will, too). What I share comes from real-world (i.e., commercial) projects. Some of the tricks I'll show I used in a P2-based coprocessor system for a medical device. The main computer sends string commands to the P2 which turns those into IO signals and low-level things that the PC can't directly control (servos, LED brightness, etc.).

    Since I plan to add other choices, would using case be a better option.

    You can't use case directly with strcomp(). What you can do, though -- and this is what I do in programs that use single string commands -- is convert the string to an index value that can be used with case. This may be a little advanced, so I'll explain it piece-by-piece.

    Step 1. Move your command strings to a list. It is very important that each string have just ONE terminating zero -- except the last which needs one extra zero for the search/indexing method.

      s_Commands            byte
      s_LedOff              byte    "ledoff", 0
      s_LedOn               byte    "ledon",  0
      s_Flash               byte    "flash",  0
      s_Help                byte    "help",   0
      EndOfList             byte    0
    

    Technically, you don't need to give each string a name, but I like to do it for flexibility.

    The next step is to take the string provided by a user and compare it to items in the command list. This uses another string function in Spin called strsize() which returns the length of a string (count of characters up to, but not including, the terminating 0).

    pub str_to_idx(p_str, p_list) : idx | len
    
      repeat 
        len := strsize(p_list)
        if (len > 0)                                                ' more strings?
          if (strcomp(p_str, p_list))                               ' match?
            return idx                                              '  return list index
          else
            p_list += len + 1                                       ' skip unmatched string
            ++idx                                                   ' update index
        else
          return -1                                                 ' string not in list
    

    This is a little tricky -- but only a little. Once you understand it you'll appreciate what it saves you when searching through a list of 20 strings versus a giant stack of if-elseif clauses. Note, too, that it's atomic which means you can use it with different lists within the same program, and they can be of any length.

    Okay, here's how it works. We pass a pointer to our string (input command from user) and a pointer to our command list. There is an unconstrained loop inside this code so that we can process the whole list without having to know how many items it has. To do that we use strsize() at the top of the list. If that is greater than zero there are still strings to process; if not we return -1 to tell the caller that string was not found. Let's assume we have a string. We use strcomp() with the list pointer. If there is a match we return the current index. Remember, the return variable (idx) is automatically set to 0 when we call this method so we don't have to initialize it manually. Let's say that the string isn't a match. We jump over it by adding the length of the current string plus one to the list pointer. Again, the plus one gets us past the current string. We also update the index when skipping a unmatched string.

    Now we can use case to process the string index.

    pub process_command(cmd)
    
      case cmd
        0     : pinlow(LED)
        1     : pinhigh(LED)
        2     : flash_led()
        3     : term.str(@s_HelpMenu)
        other : term.fstr0("\rInvalid command  \r")
    

    Remember that the position of the command string in the list determines its list index. When I build systems using this code I put frequently-used commands at the top of the list.

    Okay, take a breath and dive in. The attached program works. If you read this through a couple times and experiment with the code it will begin to make sense (again, this is not a beginner topic). I love using string commands. Long term, you'll want to get into a string parser where you can enter a complex string, break it into it's constituent parts, and then operate on them. That's a lot of fun.

  • RsadeikaRsadeika Posts: 3,896

    Thanks Jon. I just started to look at your command demo object. I am now using Spin Tools exclusively, when I ran your program, in the Spin Tools terminal, all I see is bunch of strange looking chars. I will see if I can figure out what is going on.

    Ray

  • JonnyMacJonnyMac Posts: 9,719
    edited 2026-03-08 18:00

    Check the baud rate on the terminal. I default to 230_400 because that's the default of FlexProp (which I don't really use, but test projects with when I'm going to do a public presentation). With P2 smart pins we can actually have much higher rates. DEBUG commands, for example, use 2M baud.

  • JonnyMacJonnyMac Posts: 9,719
    edited 2026-03-08 21:34

    Note, too, that it's atomic which means you can use it with different lists within the same program, and they can be of any length.

    I sometimes get frustrated when someone in the forums drops a theory but doesn't demonstrate that theory with actual code -- like I did with that comment above. In order to not be one of those "say but don't show" types, I wrote the attached program while watching the F1 race last night. My laser tag colleague (JoshW) and I are working on a specification for a new product; in our discussions about features multi-lingual support came up. The attached demo is a simple way for multi-lingual support.

    As you can see in the program there is an English section:

    dat { english }
    
    ' Commands must be clean; no leading or trailing space padding
    ' -- only one 0 after each command string
    ' -- additional 0 to signify end of list
    
      s_EnglishCmds         byte
                            byte    "off",     0
                            byte    "on",      0
                            byte    "flash",   0
                            byte    "english", 0
                            byte    "spanish", 0
                            byte    "help",    0
      EndofEnglishCmds      byte    0
    
      s_EnglishHelp         byte    13, 13
                            byte    "Commands:",  13
                            byte    "-- off",     13
                            byte    "-- on",      13
                            byte    "-- flash",   13
                            byte    "-- english", 13
                            byte    "-- spanish", 13
                            byte    "-- help",    13
                            byte    0
    
      s_EnglishBadCmd       byte    13, "-- Invalid command", 0
    

    ... and a matching Spanish section (translation by ChatGPT and Google Translate):

    dat { spanish }
    
      s_SpanishCmds         byte
                            byte    "apagado",   0
                            byte    "encendido", 0
                            byte    "destello",  0
                            byte    "ingles",    0
                            byte    "espanol",   0
                            byte    "ayuda",     0
      EndofSpanishCmds      byte    0
    
      s_SpanishHelp         byte    13, 13
                            byte    "Comandos:",    13
                            byte    "-- apagado",   13
                            byte    "-- encendido", 13
                            byte    "-- destello",  13
                            byte    "-- ingles",    13
                            byte    "-- espanol",   13
                            byte    "-- ayuda",     13
                            byte    0
    
      s_SpanishBadCmd       byte    13, "-- Comando invalido", 0
    

    There is a global variable for the language that drives language-dependent process, e.g., which command list to use or which help string to display.

    One of the nice features of the terminal in Spin Tools is that it has a line input mode; this lets you edit a line before pressing Enter which sends it to the Propeller. You need to enable line input in the Preferences dialog.

    There is no need to change the get_str() method as that will work with the Spin Tools terminal in either mode, or with PST which only does character mode. What's nice about the Spin Tools line input mode is that it has a history list. This can be helpful when testing. Show the terminal history by clicking on the down arrow, select an item, then press Enter. Easy-peasy. Note that this input line at the top of the Spin Tools terminal (STT) is only visible when in Line Input mode is enabled (it's white -- the yellow in this image is my highlight).

    Finally, the multi-lingual demo only has one output line, but in the real world (e.g., our upcoming laser tag project) we'd have more than that. No problem: we simply create a list of output strings like we created a list of command strings and then use this method -- it is a mirror for the str_to_idx() method. We have an extra parameter here for the string to show if the index is outside the bounds of the list.

    pub idx_to_str(idx, p_list, p_error) : p_str | len
    
    '' Return pointer to idx'th string in p_list
    '' -- p_error is pointer to string for bad index
    
      repeat
        len := strsize(p_list)
        if (len > 0)
          if (idx == 0)                                             ' at target?
            return p_list                                           '  return string pointer
          else
            p_list += len + 1                                       ' skip this string
            --idx                                                   ' update index
        else
          return p_error                                            ' bad index, shoe error string
    

    I've attached a simple demo that shows this in action.

Sign In or Register to comment.