Arduino: Implementing a Simple Terminal Prompt
Tags: Cpp, Arduino, Command Line

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 IDE to write Arduino programs, all code examples are in C++.

This article originally appeared at my blog admantium.com.

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:

void helpMsg() {

Serial.println("\

Please select a command

\

- LED {msg} Print msg to LED Matrix

\

- LOG {msg} Log msg

\

- EXIT Restart the program

");

}

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.

void promptMsg() {

Serial.print("> ");

}

Both methods are called during setup() .

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.

String cmd;

char car; void loop() {

if (Serial.available()) {

car = Serial.read();

cmd += String(car);

Serial.print(car);

} if (cmd.indexOf('

') > -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.

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:

void bootupMsg() {

Serial.println("+++ RADU MKI v0.2 Booting +++");

} void helpMsg() {

Serial.println("\

Please select a command

\

- LED {msg}- Print msg to LED Matrix

\

- LOG {msg}- Log msg

\

- EXIT - Restart the program

");

} 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('

') > -1) {

process();

cmd = "";

promptMsg();

}

}

Conclusion