Arduino: Implementing a Simple Terminal Prompt
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++.
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\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.
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('\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.
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\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?