Search code examples
c#serial-portconsole-applicationfirmware.net-4.8

How to have command history between RS232 Serial Port and local laptop?


I was developing a C# Console app to interact with firmware on SAS Expander card.

The card was connected via RS232 Port.

I have already sent a command and had a result on the Console App.

Now I want to have a function to record command.

When I use PuTTY to send command,

I realize that when I press UpArrow key, it will show the last command.

Here is PuTTY's result:

before press UpArrow Key

enter image description here

after press UpArrow Key

enter image description here

Here is my code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApp3
{
    class Program
    {
        [STAThread]
        static void Main()
        {

            SerialPort mySerialPort = new SerialPort("COM5");

            mySerialPort.BaudRate = 115200;
            mySerialPort.Parity = Parity.None;
            mySerialPort.StopBits = StopBits.One;
            mySerialPort.DataBits = 8;
            mySerialPort.Handshake = Handshake.None;
            mySerialPort.RtsEnable = true;
            mySerialPort.DtrEnable = true;
            mySerialPort.ReadTimeout = 2000;
            mySerialPort.WriteTimeout = 1500;
            mySerialPort.Open();
            if (mySerialPort.IsOpen)
            {

                mySerialPort.Write("\r");

            }
            mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
            while (true)
            {
                
                if (Console.KeyAvailable)
                {
                    ConsoleKeyInfo userResponse = Console.ReadKey(true);
                    List<string> Command = new List<string>();
                    if (userResponse.KeyChar.ToString() == "s")
                    {
                        Command.Add("s");
                        mySerialPort.Write("s");

                    }
                    else if (userResponse.KeyChar.ToString() == "y")
                    {
                        Command.Add("y");
                        mySerialPort.Write("y");

                    }
                    else if (userResponse.Key == ConsoleKey.UpArrow)
                    {

                        for (int i = 0; i < Command.Count; i++)
                        {
                            Console.WriteLine(Command[i]);

                        }
                        Console.ReadLine();
                        Command.Clear();
                    }
                    else
                    {
                        mySerialPort.Write("\r");
                        
                    }
                } 
            }
        }
        private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            string indata = sp.ReadExisting();
            Console.Write(indata);
        }

    }

}

What the code's result:

enter image description here

When I press UpArrow key twice, the app shows up two space to enter.

That was the second bp1> in the picture.

The picture looks like:

enter image description here

After I press enter Key, it comes out bp1> again.

I started to send sys command and it didn't get response, but it came up a space to enter.

After I press Enter and UpArrow key again, the sys command shows up.

What is the problem with my code?

Is there another way to improve?

My console App is using .Net Framework 4.7.2.


Solution

  • The better way is to use the mono/LineEditor, how kunif said in the comments. A other way is like this:

    using System;
    using System.Collections.Generic;
    using System.IO.Ports;
    
    namespace ConsoleApp3
    {
        class Program
        {
            static void Main()
            {
    
                SerialPort mySerialPort = new SerialPort("COM5");
    
                mySerialPort.BaudRate = 115200;
                mySerialPort.Parity = Parity.None;
                mySerialPort.StopBits = StopBits.One;
                mySerialPort.DataBits = 8;
                mySerialPort.Handshake = Handshake.None;
                mySerialPort.RtsEnable = true;
                mySerialPort.DtrEnable = true;
                mySerialPort.ReadTimeout = 2000;
                mySerialPort.WriteTimeout = 1500;
                // mySerialPort.NewLine = "\r";     // implement this if you use "string indata = sp.ReadLine();" in event handler
                mySerialPort.Open();
    
                //if (mySerialPort.IsOpen)
                //{
                //    mySerialPort.Write("\r");
                //}
    
                List<string> Commands = new List<string>();
                string command = "";
                int counter = 0;
                //mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                while (true)
                {
                    ConsoleKeyInfo consoleInput = Console.ReadKey();
    
                    if (consoleInput.KeyChar == '\r')
                    {
                        Commands.Add(command);
                        counter = Commands.Count - 1;
                        //mySerialPort.Write(command + "\r");
                        ClearCurrentConsoleLine();
                        command = "";
                    }
                    else if (consoleInput.Key == ConsoleKey.UpArrow)
                    {
                        if ((counter >= 0) && (counter < Commands.Count))
                        {
                            ClearCurrentConsoleLine();
                            Console.Write(Commands[counter]);
                        }
    
                        if (counter > 0)
                        {
                            counter--;
                        }
                        else 
                        {
                            counter = Commands.Count - 1;
                        }
                    }
                    else
                    {
                        command += consoleInput.KeyChar.ToString();
                    }
                }
            }
            private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
            {
                SerialPort sp = (SerialPort)sender;
                string indata = sp.ReadExisting();      // it might be better to use --> string indata = sp.ReadLine();
                Console.Write(indata);
            }
    
            private static void ClearCurrentConsoleLine()
            {
                int currentLineCursor = Console.CursorTop;
                Console.SetCursorPosition(0, Console.CursorTop);
                Console.Write(new string(' ', Console.WindowWidth));
                Console.SetCursorPosition(0, currentLineCursor);
            }
        }
    }
    

    I tested the code without the serial communication. The serial communication should work if you clear my comments.