Multiple string command

I have string1, string2, string 3........n etc
i am trying to get three commands in one go
serial monitor accepts only one command if there are more it just prints and does not proceed to action.

Can you explain what you're trying to do, and how?

I have coded strings like 'goat', 'dog', 'cat', etc. when i run the code and type in the serial monitor dog, the corresponding LED lights up. But if I type, dog,cat it does not work. It just prints these words.

We can't see your code.

What's this got to do with command line tools?

as hinted by @TheMemberFormerlyKnownAsAWOL

your post is in the wrong subforum

I moved the thread to a better place

do yourself a favour and please read How to get the best out of this forum and post accordingly (including code with tags and necessary documentation for your ask).

Maybe your µC is more of a dog person than a cat person. Merely echoing "cat" back to you is it's way of telling you that it doesn't care much for cats. Please, respect your µC's preferences and feelings about pets. How would you feel in it's silicon shoes?

By the way, this is an issue about the emotional part of the µC, not the logical part of it, so you are perfectly right not to post any code, because code is all about logic, isn't it? Just be nice to your µC and the two of you will get along well in the end.

you have to split your entry into the command words.
the process is called "to split something into tokens".
so you must split your entry and try to find your commands and then execute your commands.
You could google for following keywords

"c++ strtok"

see for tutorials or read this:

https://www.cplusplus.com/reference/cstring/strtok/

1 Like

I've not tried to do this before, seemed like a fun task. I haven't managed to break it just yet:

#define ARRSIZE(x) sizeof(x)/sizeof(x[0])
#define MAX_TOKENS 10
#define MAX_INPUT_LENGTH 30

char inputBuffer[MAX_INPUT_LENGTH];
char* tokens[MAX_TOKENS];
byte tokenIndex = 0;

char* validCommands[] = { "dog", "goat", "cat" };
void (*funcPtrs[ARRSIZE(validCommands)])() = { &dogCommand, &goatCommand, &catCommand };

void getInput() {
  while (!Serial.available());
  Serial.readBytesUntil('\n', inputBuffer, MAX_INPUT_LENGTH);
}

void tokeniseInput() {
  char* token = strtok(inputBuffer, " ,.-");
  while (token != NULL && tokenIndex < MAX_TOKENS) {
    tokens[tokenIndex++] = token;
    token = strtok(NULL, " ,.-");
    if (tokenIndex >= MAX_TOKENS)
      Serial.println(" ERR! Max Tokens Reached");
  }
}

void printTokens() {
  for (byte i = 0; i < tokenIndex; i++) {
    Serial.print("Token Stored: ");
    Serial.println(tokens[i]);
  }
}

void reset() {
  for (byte i = 0; i < MAX_INPUT_LENGTH; i++)
    inputBuffer[i] = NULL;
  for (byte i = 0; i < MAX_TOKENS; i++)
    tokens[i] = nullptr;
  tokenIndex = 0;
}

void processCommands() {
  for (byte i = 0; i < tokenIndex; i++) {
    for (byte j = 0; j < ARRSIZE(validCommands); j++) {
      if (!strcmp(tokens[i], validCommands[j])) {
        Serial.println("Valid Command Found!");
        funcPtrs[j]();
      }
    }
  }
}

void dogCommand() {
  Serial.println("Digging in your favourite flower bed");
}
void goatCommand() {
  Serial.println("Eating it's weight in grass");
}
void catCommand() {
  Serial.println("Sleeping on top of the fridge");
}

void setup() {
  Serial.begin(9600);
  Serial.println("Enter Your Commands:");
}

void loop() {
  getInput();
  tokeniseInput();
  // printTokens(); // Uncomment for debugging
  processCommands();
  reset();
}

EDIT: Added an array of function pointers and a list of valid commands. Fun times!

Output:

Enter Your Commands:
 //  Input of "the cat jumped over the dog" 
Valid Command Found!
Sleeping on top of the fridge
Valid Command Found!
Digging in your favourite flower bed

Of course, you can comment out/remove a whole bunch of serial prints if it's not wanted.

Thanks. It works fine. have to test if it executes the command. will update. thanks a lot

#define ARRSIZE(x) sizeof(x)/sizeof(x[0])
#define MAX_TOKENS 10
#define MAX_INPUT_LENGTH 30

char inputBuffer[MAX_INPUT_LENGTH];
char* tokens[MAX_TOKENS];
byte tokenIndex = 0;

char* validCommands = { "dog", "goat", "cat" };
void (*funcPtrs[ARRSIZE(validCommands)])() = { &dogCommand, &goatCommand, &catCommand };
#define blueLed 12
#define whiteLed 13
#define redLed 16

void getInput() {
while (!Serial.available());
Serial.readBytesUntil('\n', inputBuffer, MAX_INPUT_LENGTH);
}
void setup() {
Serial.begin(9600);
//Serial.println("Enter Your Commands:");
pinMode(blueLed, OUTPUT);
pinMode(whiteLed, OUTPUT);
pinMode(redLed, OUTPUT);
delay(2000);
}

void tokeniseInput() {
char* token = strtok(inputBuffer, " ,.-");
while (token != NULL && tokenIndex < MAX_TOKENS) {
tokens[tokenIndex++] = token;
token = strtok(NULL, " ,.-");
if (tokenIndex >= MAX_TOKENS)
Serial.println(" ERR! Max Tokens Reached");
}
}

void printTokens() {
for (byte i = 0; i < tokenIndex; i++) {
Serial.print("Token Stored: ");
Serial.println(tokens[i]);
}
}

void reset() {
for (byte i = 0; i < MAX_INPUT_LENGTH; i++)
inputBuffer[i] = NULL;
for (byte i = 0; i < MAX_TOKENS; i++)
tokens[i] = nullptr;
tokenIndex = 0;
}

void processCommands() {
for (byte i = 0; i < tokenIndex; i++) {
for (byte j = 0; j < ARRSIZE(validCommands); j++) {
if (!strcmp(tokens[i], validCommands[j])) {
//Serial.println("Valid Command Found!");
funcPtrsj;
}
}
}
}

void dogCommand() {
Serial.println("dog");
digitalWrite(whiteLed, HIGH);
delay (500);
digitalWrite(whiteLed, LOW);
}
void goatCommand() {
Serial.println("goat");
digitalWrite(blueLed, HIGH);
delay (500);
digitalWrite(blueLed, LOW);
}
void catCommand() {
Serial.println("cat");
digitalWrite(redLed, HIGH);
delay (500);
digitalWrite(redLed, LOW);
}

void loop() {
getInput();
tokeniseInput();
//printTokens(); // Uncomment for debugging
processCommands();
reset();
}

didn't you get a notification that your post was needing code tags? if so why did you ignore it?

please do yourself a favour and please read How to get the best out of this forum and modify your post accordingly

You should post code by using code-tags
There is an automatic function for doing this in the Arduino-IDE
just three steps

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

best regards Stefan

Reply

themakaseh
10h
#define ARRSIZE(x) sizeof(x)/sizeof(x[0])
#define MAX_TOKENS 10
#define MAX_INPUT_LENGTH 30

char inputBuffer[MAX_INPUT_LENGTH];
char* tokens[MAX_TOKENS];
byte tokenIndex = 0;

char* validCommands = { "dog", "goat", "cat" };
void (*funcPtrs[ARRSIZE(validCommands)])() = { &dogCommand, &goatCommand, &catCommand };
#define blueLed 12
#define whiteLed 13
#define redLed 16

void getInput() {
  while (!Serial.available());
  Serial.readBytesUntil('\n', inputBuffer, MAX_INPUT_LENGTH);
}
void setup() {
  Serial.begin(9600);
  //Serial.println 1("Enter Your Commands:");
  pinMode(blueLed, OUTPUT);
  pinMode(whiteLed, OUTPUT);
  pinMode(redLed, OUTPUT);
  delay(2000);
}

void tokeniseInput() {
  char* token = strtok(inputBuffer, " ,.-");
  while (token != NULL && tokenIndex < MAX_TOKENS) {
    tokens[tokenIndex++] = token;
    token = strtok(NULL, " ,.-");
    if (tokenIndex >= MAX_TOKENS)
      Serial.println(" ERR! Max Tokens Reached");
  }
}

void printTokens() {
  for (byte i = 0; i < tokenIndex; i++) {
    Serial.print("Token Stored: ");
    Serial.println(tokens[i]);
  }
}

void reset() {
  for (byte i = 0; i < MAX_INPUT_LENGTH; i++)
    inputBuffer[i] = NULL;
  for (byte i = 0; i < MAX_TOKENS; i++)
    tokens[i] = nullptr;
  tokenIndex = 0;
}

void processCommands() {
  for (byte i = 0; i < tokenIndex; i++) {
    for (byte j = 0; j < ARRSIZE(validCommands); j++) {
      if (!strcmp(tokens[i], validCommands[j])) {
        //Serial.println 1("Valid Command Found!");
        funcPtrsj;
      }
    }
  }
}

void dogCommand() {
  Serial.println("dog");
  digitalWrite(whiteLed, HIGH);
  delay (500);
  digitalWrite(whiteLed, LOW);
}
void goatCommand() {
  Serial.println("goat");
  digitalWrite(blueLed, HIGH);
  delay (500);
  digitalWrite(blueLed, LOW);
}
void catCommand() {
  Serial.println("cat");
  digitalWrite(redLed, HIGH);
  delay (500);
  digitalWrite(redLed, LOW);
}

void loop() {
  getInput();
  tokeniseInput();
  //printTokens(); // Uncomment for debugging
  processCommands();
  reset();
}

Thanks Stefan, have a good day.

@st3v3n92 :#

can you please write an easy to understand explanation of these two lines of code

Easy to understand for me means:

After reading the explanation a beginner ist able to write down a new application of the principles used in the example from scratch.
Which means she/he has really understood how it works.

best regards Stefan

the first line (should add const to the type) defines an array of pointers to cStrings. It holds 3 entries pointing respectively at the cStrings "dog", "goat" and "cat"

the second line defines an array of function pointers, functions taking no arguments and returning nothing (void), that has the same number of elements as the first array. It holds three function pointers, pointing respectively at the functions dogCommand() , goatCommand(), and catCommand()

A function pointer is a variable that stores the address of a function that can later be called through that pointer.

understanding how to define a function pointer type means understanding how you write (syntax) a "complex" type and is part of the C++ language definition. That should be studied from the specification in my view but you'll find good explanations on line, here is a first hit: :Function Pointers in C and C++ - Cprogramming.com

Hi J-M-L,

thank you for your explanation. My real point embedded in my question is:
This way of coding is effective and elegant but

needs a lot of knowledge about advanced programming techniques and has an inbuild danger of programming bugs because it uses pointers.

IMHO this is not good for a beginner. A beginner would have to read quite a lot, would have to collect experience with function pointers and only after that will be able to write code using function-pointers from scratch.

I took a look into the tutorial you linked to
quote:

Function Pointer Syntax

The syntax for declaring a function pointer might seem messy at first, but in most cases it's really quite straight-forward once you understand what's going on. Let's look at a simple example:

1 void (*foo)( int );

In this example, foo is a pointer to a function taking one argument, an integer, and that returns void. It's as if you're declaring a function called "*foo", which takes an int and returns void; now, if *foo is a function, then foo must be a pointer to a function. (Similarly, a declaration like int *x can be read as *x is an int, so x must be a pointer to an int.)

The key to writing the declaration for a function pointer is that you're just writing out the declaration of a function but with (*func_name) where you'd normally just put func_name.

well this is a wayyyy too fast explanation for a beginner which uses a lot of technical terms that haven't been explained before.

You might say "yes of course the reader must have a basic knowledge about these terms"
Yes and this is the exact reason why it is NOT good for a beginner. Way more knowledge needed than with other solutions.

Writing a beginner-friendly explanation for this code would need minimum five pages and 10 demo-codes.

best regards Stefan

fair point although I've not seen @themakaseh stating s/he was a complete beginner.

I agree it's not very beginner friendly, although it gets the job done!

One improvement could be to type def the function pointers to hide the scaryness a little.

The second, if a beginner really likes how simple the useage is (even if they dont understand the what and how) would be essentially 'Dont modify whats there, only add (matching!) strings and functions in the same style'.

i.e I can easily replace the &dogCommand with &turnOnLights or it wouldn't be too hard to add a "fish" string and a &fishCommand to the end.

Again, I agree that it's not ideal for a beginner, but providing the beginner is careful not to break things, the tools don't have to be reserved for advanced programmers.

Plenty of people use internal combustion engines without knowing how or why they work, or how to fix them when they break. You just have to be careful :smiley: We wouldn't say "Sorry, you can only use a car if you can tell me precisely how it works, and then build me one from scratch" and I see this in a sinilar way (in this particular case). I'm not saying all beginners should use function pointers, but I am saying all beginners shouldn't be scared to at least try them out.

You only really to memorise the syntax and know that they work, you dont really need to know why or how at the beginner stage.

OP if you dont understand parts or if you do break something (totally fine!) just head back here and ask us.

the other way for doing this that would be easier to read, would be a better defined command language, with clear determination of what is an end marker for a command. Here it seems OP wants the end of the line or a comma to denote the end of the command (may be even a timeout?)

The code could then be "simplified" to read until an end marker (I would suggest to study Serial Input Basics to handle this) and then you just have to consider only one command in the received buffer. No tokenisation needed. Just have a big list of nested if to compare with known keywords and call the right function

if (strcmp(buffer, "dog") == 0) dogFunction(); 
else
if (strcmp(buffer, "goat") == 0) goatFunction(); 
else
if (strcmp(buffer, "cat") == 0) catFunction(); 
else
errorFunction();