Serial data string.equals wild cards?

Hi,
I am trying to receive serial commands for a project, where each received command ends with \n.
This works well, and I can check which command is received using If else statements.

But one command is 3 fixed characters, say CMD, followed by a 3 digit value (0 to 360).
So the actual command received is CMD***\n where *** is (000-360).

Using the following code, I can easily receive data until a \n is received then check for the command using if statements:

if (Serial1.available()) {
    command = Serial1.readStringUntil('\n');
    command.trim();

    if (command.equals("CMD")) {
      Serial1.write("OK. DO SOMETHING\n");
    }
    else if (command.equals("LED")) {
      Serial1.write("OK, turn on LED");
    }
    else {
      Serial1.write("BAD COMMAND\n");
}

So the above code will receive serial commands until \n is received. Then check for which command is received.

But the command CMDxxx, where xxx is a number 0 to 360.

This does not work, but can I use a wild card in the

if (command.equals("CMD")) {

Like:

 if (command.equals("CMD",'***')) {

So it will still trap the CMD command with 3 other characters directly after it. But not actually check the 3 numbers apart from that they exist.
I then need to strip out the 3 numbers.

Does that make sense? Or have I just confused the issue here?

Thanks

look this over


void loop()
{
    if (Serial.available ())  {
        char buf [90];
        int n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';     // terminate with null

        char cmd [10];
        int  val = 0;
        sscanf (buf, "%s %d", cmd, & val);

        if (! strcmp (cmd, "CMD"))  {
            Serial.print   (" cmd ");
            Serial.println (val);
        }
        else if (! strcmp (cmd, "LED"))  {
            digitalWrite (LED_BUILTIN, val);
        }
    }
}


void setup()
{
    Serial.begin (9600);
    pinMode (LED_BUILTIN, OUTPUT);
}

CMD123

doesn't work. But

CMD 123

does.

Curiously the obvious changing to

        sscanf (buf, "%s%d", cmd, & val);

doesn't seem to work either?

I'm not a fan of sscanf(), so I don't know if it can be made to work in this case.

a7

sscanf (buf, "%3s%d", cmd, & val);

works but doesn't separating the cmd from an argument make more sense, or are there going to be #'d commands CMD1, CMD1, ...?

You could use

if (command.indexOf("CMD") > -1) {
  // extract the value and what follows
}

And then extract the value as you like.

1 Like

Maybe so. Maybe @dogboy has the ability to change both sides of the transaction to suit your idea of what makes more sense.

sscanf() can be used as you have shown with the new format argument.

I read this in post #1:

I don't see it as either making sense or not making sense. It is what it is.

a7

I will have around 10 commands, all are three characters. Only one command will have a parameter to set a a particular value (0 to 360).
As in CMDxxx, but I suppose I could split it and receive the CMD first then look for the xxx and convert it to a value, but just seems a bit messy.

If all commands are 3 characters long except CMDxxx which is 6, it is not that complicated to do.
If the string read has more than 3 characters, you confirm that it contains "CMD" as I suggested in #5 and then extract the number.
Otherwise (it would have 3 characters), you execute the statements corresponding to those commands.

Hi Maximo,

Please can you explain this in more detail.
I half understand what its doing but not 100% sure if I its right.

Thanks

OK, I now understand that after reading the Arduino docs.
Only thing I would need to do is to check that when the CMDxxx is received that the xxx is only 3 characters. So the Indexof traps the CMD part but it would also work with CMDxxxxxxxx...., so I need to check the total length of the command received.

Seems pretty straight forward. And works!

Thanks

As the saying goes

The first pattern that comes to mind is

CMD([0-9]{3})|LED|FOO|BAR

but the Arduino-listed Regexp library does not support | for alternates (nor {} for repetitions, although that is easily worked-around in this case).

It's simpler to just startsWith instead of equals and some logic

if (command.startsWith("CMD")) {
  command = command.substring(3);
  if (command.length()) {
    int num = command.toInt();
    if (num > 360) {
      Serial.println("max is 360");
    } else if (num < 0) {
      Serial.println("not negative");
    } else if (num > 0 || command.startsWith("0")) {
      Serial.println("do CMD: " + command);
    } else {
      Serial.println("invalid CMD: " + command);
    }
  } else {  // "".toInt() returns 0 as well   
    Serial.println("missing CMD number");
  }
} else if (command.equals("LED")) {
  Serial.println("OK, turn on LED");
} else {
  Serial.println("no match");
}

BTW, startsWith() is equivalent to indexOf() == 0. You don't want to match XCMD for example, where the index is 1.

I just used

else if ((command.indexOf("ATM") > -1) && (command.length()==6)){
      Serial1.println("Now this traps the command ATMxxx");
    }

This works perfectly, just one line.

That matches for example 3_ATMx ; do you want that?

Ah, no!

This 'Seems' to work:

else if ((command.startsWith("ATM")) && (command.length()==6)){
      Serial1.println("Traps ATMxxx");
    }

Trying it now, seems ok.

As long as for example seven is always 007 and not just 7 nor 07: ATM007 only. If you want to support ATM7 then it needs more code.

Yes, that particular command will always be in the format ATMxxx so 7 will be 007.
The commands are coming from Labview, so will be tightly controlled.

Thanks to everyone for your replies. Its much appreciated.

@dogboy i think you are making this over complicated. it's pretty routine to read a cmd with some argument of arbitrary length. since it sounds like you want a cmd that specifies a value from 0 to 360 degrees it seems unnecessary to require that those values be specified by one of 360 commands: CMD001, CMD002, ...CMD360.

and what about the possibility of specifying negative values, relative value.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.