Building a Serial Command Line Interface

In this article we will build a very basic CLI ( Command Line Interface) and use putty to navigate it.
In some cases you would want to be able to change custom built settings on your micro controller such as a SSID and wirless network password on an ESP8266 or even setting an IP address or set a username and password for a web front-end.There are obviously many other reasons for this.

The most common part of a CLI is to be able to receive commands entered by a user and then make sense of it by either rejecting the entered command if its invalid or translating it into an action if it’s valid.

Usually you would want to validate the input after pressing enter. So for starters we need allow the user to enter data and tell the micro controller to keep receiving user input until the enter key was pressed.

We do this by reading the serial port bytes and add the characters to the incoming command until the entered character was detected. When the serial reader receives incoming text they will be in bytes representing the ASCII code of the incoming characters. The ASCII code for enter is the number 13. So all we have to do is to read the incoming serial data until we receive 13. From then on out we should further create some if statements to determine if the command was valid or not.

Example:

if (incomingByte != 13)
{ "Keep Building the incomming command line input"; } 
else
{"Do something with the command"}

Before we continue let’s take one step back. Seeing we all humans and we are bound to make mistakes there will most certainly be a time where you created a typo.
Problem is all keys on a keyboard has an ASCII character.

So say we wanted to type the command “set password” but we accidentally typed “set passwortd” So we would typically press backspace twice to erase the the “t” and then press “d” to correct our command. When you do this it will include backspace as part of the command and the command would look like this
“set passwortd [BACKSPACE] [BACKSPACE] d”. This means your command will be invalid because it does not expect [BACKSPACE] in the command.

Fortunately this is easy to fix by ignoring the byte value of 127. 127 is the decimal ASCII code for backspace.

See below ASCII table.


if (incomingByte != 13)
{ 
 if(incomingByte != 127){cmdBuild = cmdBuild + incomingChar;}else{cmdBuild= cmdBuild.substring(0,cmdBuild.length()-1);}
} 
else
{"Do something with the command"}


Once we detected that entered was pressed we can continue to validate the command simply by comparing the sting to our existing list of known commands.

Example:
The below will compare the incoming text with the command called “help”

if (cmdBuild=="help"){Printhelp();CommandValid=1;}

Here is a full example of the code:

int incomingByte;
char incomingChar;
int CommandValid;
String cmdBuild;
String StringValue;
void setup() {
Serial.begin(115200);
Printwelcome();
}

void loop() {
  
  readincommingserial();

}

void Printwelcome()
{
  Serial.println("");
  Serial.println("*********************************************************************");
  Serial.println("** Welcome to the serial command line console.                     **");
  Serial.println("** This Arduino sketch will emulate a serial configurable console. **");
  Serial.println("*********************************************************************");
  Serial.println("Type help to get a list of commands.");
  Serial.println("");
}
void readincommingserial()
{
  
if (Serial.available() > 0) 
  {
   incomingByte = Serial.read();
   incomingChar = char(incomingByte);
      if (incomingByte != 13)
                {
                 if(incomingByte != 127){cmdBuild = cmdBuild + incomingChar;}else{cmdBuild= cmdBuild.substring(0,cmdBuild.length()-1);}//Remove Backspaces and the last mistake chracter
                }
                else
                {
                  Serial.println("");
                  if (cmdBuild=="help"){Printhelp();CommandValid=1;}
                  if (cmdBuild=="welcome"){Printwelcome();CommandValid=1;}
                  if (cmdBuild=="clear"){clearAndHome();CommandValid=1;}
                  if (cmdBuild=="progress"){dosworking();CommandValid=1;}
                  if (cmdBuild=="print stringvalue"){Printstringvalue();CommandValid=1;}

                  
                  
                  if (cmdBuild.substring(0,16)=="set stringvalue "){Setstringvalue();CommandValid=1;}                                                   
                  //Check for invalid command.;
                  if(CommandValid==0){Serial.println("'"  + cmdBuild + "' is not recognized as an internal or external command.");}
                  //Clear Last Command
                  cmdBuild="";
                  CommandValid=0;
                }
     //Repeat the key strokes
     Serial.print(incomingChar);    
     //Serial.print(incomingByte);//Uncomment this is for debugging purposes.
  }
}  

void Printhelp()
{
  Serial.println("Help - Displays a list of valid commands.");
  Serial.println("------------------------------------------------------------------");
  Serial.println("help                 Shows this help screen.");
  Serial.println("welcome              Reprint the welcome screen.");
  Serial.println("clear                Clear the console screen");
  Serial.println("progress             DOS style working in progress");
  Serial.println("set stringvalue [n]  Set the integer to a value I.E set integer 1.");
  Serial.println("print stringvalue    Print the current value of the string.");
}
void Setstringvalue()
{
  StringValue = cmdBuild.substring(16,1000);
  StringValue.trim();
  Serial.println("String Value has been set to " +  StringValue);

}
void Printstringvalue()
{
  Serial.println("The current string value is " + StringValue);
}
void clearAndHome()
{
  Serial.write(27);
  Serial.print("[2J"); // clear screen
  Serial.write(27);
  Serial.print("[H"); // cursor to home
}
void dosworking()
{
  int MyDelay=50;
  int progress=0;
  for (int i=0; i <= 10; i++)
  {

  progress = i * 10;
  
  Serial.println("|");
  Serial.print(progress);Serial.print("% complete.");  
  delay(MyDelay);
  clearAndHome();
  
  Serial.println("/");
  Serial.print(progress);Serial.print("% complete."); 
  delay(MyDelay);
  clearAndHome();
  
  Serial.println("-");
  Serial.print(progress);Serial.print("% complete."); 
  delay(MyDelay);
  clearAndHome();
  
  Serial.println("\\");
  Serial.print(progress);Serial.print("% complete."); 
  delay(MyDelay);
  clearAndHome();
 
  }
  Serial.println("100% Done!");
  
  

}

Some extra notes.
Any good CLI will allow the user to do clear the screen. When a CLI utility such as putty detect the following input that was output by the “server” as a response will clear the putty console. This is off coarse handy when there’s too much info on the screen that’s diluting what ever info you are looking at.

void clearAndHome()
{
  Serial.write(27);
  Serial.print("[2J"); // clear screen
  Serial.write(27);
  Serial.print("[H"); // cursor to home
}

You May Also Like

About the Author: Martin Viljoen

Leave a Reply

Your email address will not be published. Required fields are marked *