3
votes

I would like to implement an interactive shell via UART serial port for Arduino, with pure C++ OOP style code. But I think if there are too many if-else judgements when judging the user input commands in the code, it will be a bit ugly,

So I would like to ask that, is there any way to avoid using if-else statement? For example,

BEFORE:

while(Serial.available())
{
    serialReceive = Serial.readString();// read the incoming data as string
    Serial.println(serialReceive);
}

if(serialReceive.equals("factory-reset"))
{
    MyService::ResetSettings();
}
else if(serialReceive.equals("get-freeheap"))
{
    MyService::PrintFreeHeap();
}
else if(serialReceive.equals("get-version"))
{
    MyService::PrintVersion();
}

AFTER:

while(Serial.available())
{
    serialReceive = Serial.readString();// read the incoming data as string
    Serial.println(serialReceive);
}

MagicClass::AssignCommand("factory-reset", MyService::ResetSettings);
MagicClass::AssignCommand("get-freeheap", MyService::PrintFreeHeap);
MagicClass::AssignCommand("get-version", MyService::PrintVersion);
1
If you are still interested, I wrote a related question on code review : codereview.stackexchange.com/questions/260122/…Eric Duminil

1 Answers

4
votes

You can have an array that stores a function pointer along with the string that triggers the command (you can create a struct to store both).

Unfortunately Arduino does not support the std::vector class so for my example I will use c type arrays. However there is a library for Arduino that adds some STL support for Arduino https://github.com/maniacbug/StandardCplusplus (also with this library you can use the functional library to make passing functions as arguments easier)

//struct that stores function to call and trigger word (can actually have spaces and special characters
struct shellCommand_t
{
  //function pointer that accepts functions that look like "void test(){...}"
  void (*f)(void);
  String cmd;
};

//array to store the commands
shellCommand_t* commands;

With this you can either initialize the command array to one size on start or resize it every time you add a command, it just depends on your use case.

A basic function that assumes you have already allocated enough space in the array for adding a command could look like this

int nCommands = 0;
void addCommand(String cmd, void (*f)(void))
{
  shellCommand_t sc;
  sc.cmd = cmd;
  sc.f = f;

  commands[nCommands++] = sc;
}

Then inside your setup function you can add your commands in a similar fashion as you have above

addCommand("test", test);
addCommand("hello world", helloWorld);

Lastly in your loop function you can use a for loop to look through all of the commands check the input string against all of the command strings.

You can call the function of the matched command like this

(*(commands[i].f))();