Arduino: Implementing a Simple Terminal Prompt
Tags: arduino, cpp
Published: 2021-09-13 05:40:28

Arduinos are great microcontroller for your embedded projects. During one of my projects, I wanted to have a simple command prompt to trigger commands at the Arduino, for example to print a message on a LCD display, or to move a servo motor with attached ultrasonic sensor. It is easy enough to listen to a single char and take this as a command, but if you come from Linux or Mac, you are used to have rich CLI tools.

 In the following article, I explain how to build a simple terminal prompt for your Arduino. I'm using the [Platform IO](https://platformio.org/) IDE to write Arduino programs, all code examples are in C++.

_This article originally appeared at my blog [admantium.com](https://admantium.com/blog/micro10_tutorial_arduino_terminal_prompt/)_.

# Terminal Prompt Basis

When the Arduino boots, it should show a small bootup message, and then list all available commands. Commands should provide some information about how to operate them. Then, when I enter any command, or more specifically, when I press any button, I want to see the change immediately. Pressing enter terminates the command and sends it to the Arduino for processing.

To start the prompt, the following method is used:

```cpp
void helpMsg() {
  Serial.println("\
Please select a command\n\
- LED {msg}     Print msg to LED Matrix \n\
- LOG {msg}     Log msg \n\
- EXIT          Restart the program\n");
}
```

We define a multiline string, expressed as String literal, and print it to the Serial.

Next comes the prompt, to indicate that input is required by the user. Use any input prompt signs you like.

```cpp
void promptMsg() {
  Serial.print("> ");
}
```

Both methods are called during `setup()`.

```cpp
void setup() {
  Serial.begin(9600);
  helpMsg();
  promptMsg();
}

void loop() { /* ... */ }
```

Ok, first part is done. Let's continue with reading the input.

## Reading Input

The Arduino library offers many methods to read input. The most ubiquitous one is `Serial.read()` which emits the Serial buffer by one byte and returns a `char`. We need to use this method, and to fulfill the requirement of immediate feedback, we cannot use any `delay()` method. Yet, at the same time, we also need to wait for the command to finish, need to wait for the user to press the enter button.

The solution is to read any input immediately when it is available, to add this input to a buffer, and also to print it back to the serial.

```cpp
String cmd;
char car;

void loop() {  
  if (Serial.available()) {
    car = Serial.read();
    cmd += String(car);
    Serial.print(car);
  }

  if (cmd.indexOf('\n') > -1) {
    process();
    cmd = "";
    promptMsg();
  }
}
```

In these lines, we read the User input with `Serial.read()`, and store the value in a `Char` variable. This char is added to the `String` `cmd`, and also printed to the screen.

Afterwards, we check that the `cmd` string contains a new-line character, which means that the user has pressed enter. If yes, we call the method `process()` - see next section - and then reset the `cmd` and show the prompt again.

## Command Processing

The `process()` function is called when the user pressed Enter. It investigates the entered `cmd` and decides what to do. Again, we use methods from the `String` class. With `.indexOf()` we can determine if the string contains another substring. With `.substring`, we extract parts form the string.

In the following lines, we check if the `cmd` contains any of the keywords `LED`, `LOG` or `EXIT`. For `LED` and `LOG`, we also check that there is any payload following the command. If there is a match, we extract the payload form the `cmd` string in the indices `[4, cmd.length() -2]`, and process it further. For demonstration purposes, here we only print the match.

```cpp
void process() {
  int index;
  if((index = cmd.indexOf("LOG")) > -1 && cmd.length() > 4) {
    String msg = cmd.substring(index + 4, cmd.length() -2);
    Serial.println("LOG >>" + msg + "<<");
  } else if((index = cmd.indexOf("LED")) > -1 && cmd.length() > 4) {
    String msg = cmd.substring(index + 4, cmd.length() -2 );
    Serial.println("LED >>" + msg + "<<");
    printStringOnMatrix(msg);
  } else if((index = cmd.indexOf("EXIT")) > -1) {
    Serial.println("Reboot ...");
  }
}
```

In all other cases, nothing happens - I'm not concerned to capture any input errors for a small DIY project.

## Complete Program

Here is the complete program:

```cpp
void bootupMsg() {
  Serial.println("+++ RADU MKI v0.2 Booting +++");
}

void helpMsg() {
  Serial.println("\
Please select a command\n\
- LED {msg}- Print msg to LED Matrix \n\
- LOG {msg}- Log msg \n\
- EXIT - Restart the program\n");
}

void promptMsg() {
  Serial.print("> ");
}

void process() {
  int index;
  if((index = cmd.indexOf("LOG")) > -1) {
    String msg = cmd.substring(index + 4, cmd.length() -2);
    Serial.println("LOG >>" + msg + "<<");
  } else if((index = cmd.indexOf("LED")) > -1) {
    String msg = cmd.substring(index + 4, cmd.length() -2);
    Serial.println("LED >>" + msg + "<<");
    printStringOnMatrix(msg);
  } else if((index = cmd.indexOf("EXIT")) > -1) {
    Serial.println("Reboot ...");
    reboot();
  }
}

void setup() {
  Serial.begin(9600);
  initMatrix();
  bootupMsg();
  helpMsg();
  promptMsg();
}

void loop() {  
  if (Serial.available()) {
    car = Serial.read();
    cmd += String(car);
    Serial.print(car);
  }

  if (cmd.indexOf('\n') > -1) {
    process();
    cmd = "";
    promptMsg();
  }
}
```

## Conclusion

This article showed how to implement a simple terminal program on your Arduino. With it, you can connect your Arduino to the seral monitor, see a list of commands and start interacting with it. In my projects, I attached a LED dot matrix, and any input text with the `LED` prefix is printed. And another command triggers the measurement with an ultrasonic sensor. Now, how will you utilize a terminal program in your project?