Example #1
0
        /// <summary>
        /// Validates and processes any command-line arguments that were passed in.  Invalid arguments will halt program execution.
        /// </summary>
        /// <param name="args">Command-line parameters</param>
        static void ProcessArguments(string[] args)
        {
            port.name = String.Empty;

            // switch to lower case and remove '/', '--' and '-' from beginning of arguments - we can process correctly without them
            for (int i = 0; i < args.Count(); i++)
            {
                args[i] = (args[i].TrimStart('/', '-')).ToLower();
            }

            // sort the parameters so that they get processed in order of priority (i.e. 'quiet' option gets processed before something that would normally result in console output, etc.)
            Array.Sort(args, new ArgumentSorter());

            // iterate through command-line arguments
            foreach (string arg in args)
            {
                // split argument into components based on 'key:value' formatting
                string[] argument = arg.Split(':');

                // help
                if (argument[0].StartsWith("h") || argument[0].StartsWith("?"))
                {
                    ShowHelp();
                    ExitProgram(silent: true);
                }

                // list available ports
                else if (argument[0].StartsWith("l"))
                {
                    // get a list of all available ports
                    availablePorts = (SimplySerial.GetSerialPorts()).OrderBy(p => p.num).ToList();

                    if (availablePorts.Count >= 1)
                    {
                        Console.WriteLine("\nPORT\tVID\tPID\tDESCRIPTION");
                        Console.WriteLine("------------------------------------------------------------");
                        foreach (ComPort p in availablePorts)
                        {
                            Console.WriteLine("{0}\t{1}\t{2}\t{3}",
                                              p.name,
                                              p.vid,
                                              p.pid,
                                              (p.board.isCircuitPython) ? (p.board.make + " " + p.board.model) : p.description
                                              );
                        }
                        Console.WriteLine("");
                    }
                    else
                    {
                        Console.WriteLine("\nNo COM ports detected.\n");
                    }

                    ExitProgram(silent: true);
                }

                // quiet (no output to console other than comes in via serial)
                else if (argument[0].StartsWith("q"))
                {
                    SimplySerial.Quiet = true;
                }

                // the remainder of possible command-line arguments require two parameters, so let's enforce that now
                else if (argument.Count() < 2)
                {
                    ExitProgram(("Invalid or incomplete argument <" + arg + ">\nTry 'ss.exe help' to see a list of valid arguments"), exitCode: -1);
                }

                // preliminary validate on com port, final validation occurs towards the end of this method
                else if (argument[0].StartsWith("c"))
                {
                    string newPort = argument[1].ToUpper();

                    if (!argument[1].StartsWith("COM"))
                    {
                        newPort = "COM" + argument[1];
                    }
                    port.name   = newPort;
                    autoConnect = AutoConnect.ONE;
                }

                // validate baud rate, terminate on error
                else if (argument[0].StartsWith("b"))
                {
                    // these are the baud rates we're supporting for now
                    string[] availableBaudRates = new string[] { "1200", "2400", "4800", "7200", "9600", "14400", "19200", "38400", "57600", "115200" };

                    if (availableBaudRates.Contains(argument[1]))
                    {
                        baud = Convert.ToInt32(argument[1]);
                    }
                    else
                    {
                        ExitProgram(("Invalid baud rate specified <" + argument[1] + ">"), exitCode: -1);
                    }
                }

                // validate parity, terminate on error
                else if (argument[0].StartsWith("p"))
                {
                    if (argument[1].StartsWith("e"))
                    {
                        parity = Parity.Even;
                    }
                    else if (argument[1].StartsWith("m"))
                    {
                        parity = Parity.Mark;
                    }
                    else if (argument[1].StartsWith("n"))
                    {
                        parity = Parity.None;
                    }
                    else if (argument[1].StartsWith("o"))
                    {
                        parity = Parity.Odd;
                    }
                    else if (argument[1].StartsWith("s"))
                    {
                        parity = Parity.Space;
                    }
                    else
                    {
                        ExitProgram(("Invalid parity specified <" + argument[1] + ">"), exitCode: -1);
                    }
                }

                // validate databits, terminate on error
                else if (argument[0].StartsWith("d"))
                {
                    int newDataBits = Convert.ToInt32(argument[1]);

                    if ((newDataBits > 3) && (newDataBits < 9))
                    {
                        dataBits = newDataBits;
                    }
                    else
                    {
                        ExitProgram(("Invalid data bits specified <" + argument[1] + ">"), exitCode: -1);
                    }
                }

                // validate stopbits, terminate on error
                else if (argument[0].StartsWith("s"))
                {
                    if (argument[1] == "0")
                    {
                        stopBits = StopBits.None;
                    }
                    else if (argument[1] == "1")
                    {
                        stopBits = StopBits.One;
                    }
                    else if (argument[1] == "1.5")
                    {
                        stopBits = StopBits.OnePointFive;
                    }
                    else if (argument[1] == "2")
                    {
                        stopBits = StopBits.Two;
                    }
                    else
                    {
                        ExitProgram(("Invalid stop bits specified <" + argument[1] + ">"), exitCode: -1);
                    }
                }

                // validate auto connect, terminate on error
                else if (argument[0].StartsWith("a"))
                {
                    if (argument[1].StartsWith("n"))
                    {
                        autoConnect = AutoConnect.NONE;
                    }
                    else if (argument[1].StartsWith("o"))
                    {
                        autoConnect = AutoConnect.ONE;
                    }
                    else if (argument[1].StartsWith("a"))
                    {
                        autoConnect = AutoConnect.ANY;
                    }
                    else
                    {
                        ExitProgram(("Invalid auto connect setting specified <" + argument[1] + ">"), exitCode: -1);
                    }
                }

                // an invalid/incomplete argument was passed
                else
                {
                    ExitProgram(("Invalid or incomplete argument <" + arg + ">\nTry 'ss.exe -help' to see a list of valid arguments"), exitCode: -1);
                }
            }

            Console.Clear();
            if (autoConnect == AutoConnect.ANY)
            {
                Output("<<< Attemping to connect to any avaiable COM port.  Use CTRL-X to cancel >>>");
            }
            else if (autoConnect == AutoConnect.ONE)
            {
                Console.Clear();
                if (port.name == String.Empty)
                {
                    Output("<<< Attempting to connect to first available COM port.  Use CTRL-X to cancel >>>");
                }
                else
                {
                    Output("<<< Attempting to connect to " + port.name + ".  Use CTRL-X to concel >>>");
                }
            }

            // if we made it this far, everything has been processed and we're ready to proceed!
        }
Example #2
0
        static void Main(string[] args)
        {
            // process all command-line arguments
            ProcessArguments(args);

            // set up keyboard input for program control / relay to serial port
            ConsoleKeyInfo keyInfo = new ConsoleKeyInfo();

            Console.TreatControlCAsInput = true; // we need to use CTRL-C to activate the REPL in CircuitPython, so it can't be used to exit the application

            // this is where data read from the serial port will be temporarily stored
            string received = string.Empty;

            //main loop - keep this up until user presses CTRL-X or an exception takes us down
            do
            {
                // first things first, check for (and respect) a request to exit the program via CTRL-X
                if (Console.KeyAvailable)
                {
                    keyInfo = Console.ReadKey(intercept: true);
                    if ((keyInfo.Key == ConsoleKey.X) && (keyInfo.Modifiers == ConsoleModifiers.Control))
                    {
                        Output("\n<<< SimplySerial session terminated via CTRL-X >>>");
                        ExitProgram(silent: true);
                    }
                }

                // get a list of available ports
                availablePorts = (SimplySerial.GetSerialPorts()).OrderBy(p => p.num).ToList();

                // if no port was specified/selected, pick one automatically
                if (port.name == String.Empty)
                {
                    // if there are com ports available, pick one
                    if (availablePorts.Count() >= 1)
                    {
                        // first, try to default to something that we assume is running CircuitPython
                        SimplySerial.port = availablePorts.Find(p => p.board.isCircuitPython == true);

                        // if that doesn't work out, just default to the first available COM port
                        if (SimplySerial.port.name == null)
                        {
                            SimplySerial.port = availablePorts[0];
                        }
                    }

                    // if there are no com ports available, exit or try again depending on autoconnect setting
                    else
                    {
                        if (autoConnect == AutoConnect.NONE)
                        {
                            ExitProgram("No COM ports detected.", exitCode: -1);
                        }
                        else
                        {
                            continue;
                        }
                    }
                }

                // if a specific port has been selected, try to match it with one that actually exists
                else
                {
                    bool portMatched = false;

                    foreach (ComPort p in availablePorts)
                    {
                        if (p.name == port.name)
                        {
                            portMatched = true;
                            port        = p;
                            break;
                        }
                    }

                    // if the specified port is not available, exit or try again depending on autoconnect setting
                    if (!portMatched)
                    {
                        if (autoConnect == AutoConnect.NONE)
                        {
                            ExitProgram(("Invalid port specified <" + port.name + ">"), exitCode: -1);
                        }
                        else
                        {
                            continue;
                        }
                    }
                }

                // if we get this far, it should be safe to set up the specified/selected serial port
                serialPort = new SerialPort(port.name, baud, parity, dataBits, stopBits)
                {
                    Handshake    = Handshake.None, // we don't need to support any handshaking at this point
                    ReadTimeout  = 1,              // minimal timeout - we don't want to wait forever for data that may not be coming!
                    WriteTimeout = 250,            // small delay - if we go too small on this it causes System.IO semaphore timeout exceptions
                    DtrEnable    = true,           // without this we don't ever receive any data
                    RtsEnable    = true            // without this we don't ever receive any data
                };

                // attempt to open the serial port, deal with failures
                try
                {
                    serialPort.Open();
                }
                catch (Exception e)
                {
                    // if auto-connect is disabled than any exception should result in program termination
                    if (autoConnect == AutoConnect.NONE)
                    {
                        if (e is UnauthorizedAccessException)
                        {
                            ExitProgram((e.GetType() + " occurred while attempting to open " + port.name + ".  Is this port already in use in another application?"), exitCode: -1);
                        }
                        else
                        {
                            ExitProgram((e.GetType() + " occurred while attempting to open " + port.name + "."), exitCode: -1);
                        }
                    }

                    // if auto-connect is enabled, prepare to try again
                    serialPort.Dispose();
                    Thread.Sleep(1000); // putting a delay here to avoid gobbling tons of resources thruogh constant high-speed re-connect attempts
                    continue;
                }

                // if we get this far, clear the screen and send the connection message if not in 'quiet' mode
                Console.Clear();
                if (!SimplySerial.Quiet)
                {
                    Console.WriteLine(("<<< SimplySerial v{0} connected via {1} >>>\n" +
                                       "Settings  : {2} baud, {3} parity, {4} data bits, {5} stop bit{6}, auto-connect {7}.\n" +
                                       "Device    : {8}{9} {10}{11}{12}\n" +
                                       "---\n\nUse CTRL-X to exit.\n"),
                                      version,
                                      port.name,
                                      baud,
                                      (parity == Parity.None) ? "no" : (parity.ToString()).ToLower(),
                                      dataBits,
                                      (stopBits == StopBits.None) ? "0" : (stopBits == StopBits.One) ? "1" : (stopBits == StopBits.OnePointFive) ? "1.5" : "2", (stopBits == StopBits.One) ? "" : "s",
                                      (autoConnect == AutoConnect.ONE) ? "on" : (autoConnect == AutoConnect.ANY) ? "any" : "off",
                                      port.board.make,
                                      (port.board.make == "VID") ? ":" + port.vid : "",
                                      port.board.model,
                                      (port.board.model == "PID") ? ":" + port.pid : "",
                                      (port.board.isCircuitPython) ? " (CircuitPython-capable)" : ""
                                      );
                }

                // this is the core functionality - loop while the serial port is open
                while (serialPort.IsOpen)
                {
                    try
                    {
                        // process keypresses for transmission through the serial port
                        if (Console.KeyAvailable)
                        {
                            // determine what key is pressed (including modifiers)
                            keyInfo = Console.ReadKey(intercept: true);

                            // exit the program if CTRL-X was pressed
                            if ((keyInfo.Key == ConsoleKey.X) && (keyInfo.Modifiers == ConsoleModifiers.Control))
                            {
                                Output("\n<<< SimplySerial session terminated via CTRL-X >>>");
                                ExitProgram(silent: true);
                            }

                            // properly process the backspace character
                            else if (keyInfo.Key == ConsoleKey.Backspace)
                            {
                                serialPort.Write("\b");
                                Thread.Sleep(150); // sort of cheating here - by adding this delay we ensure that when we process the receive buffer it will contain the correct backspace control sequence
                            }

                            // everything else just gets sent right on through
                            else
                            {
                                serialPort.Write(Convert.ToString(keyInfo.KeyChar));
                            }
                        }

                        // process data coming in from the serial port
                        received = serialPort.ReadExisting();

                        // if anything was received, process it
                        if (received.Length > 0)
                        {
                            // properly process backspace
                            if (received == ("\b\x1B[K"))
                            {
                                received = "\b \b";
                            }

                            // write what was received to console
                            Console.Write(received);
                        }
                    }
                    catch (Exception e)
                    {
                        if (autoConnect == AutoConnect.NONE)
                        {
                            ExitProgram((e.GetType() + " occurred while attempting to read/write to/from " + port.name + "."), exitCode: -1);
                        }
                        else
                        {
                            Output("\n<<< Communications Interrupted >>>\n");
                        }
                        serialPort.Dispose();
                        Thread.Sleep(2000); // sort-of arbitrary delay - should be long enough to read the "interrupted" message
                        Console.Clear();
                        if (autoConnect == AutoConnect.ANY)
                        {
                            port.name = String.Empty;
                            Output("<<< Attemping to connect to any avaiable COM port.  Use CTRL-X to cancel >>>");
                        }
                        else if (autoConnect == AutoConnect.ONE)
                        {
                            Console.Clear();
                            Output("<<< Attempting to re-connect to " + port.name + ". Use CTRL-X to cancel >>>");
                        }
                        break;
                    }
                }
            } while (autoConnect > AutoConnect.NONE);

            // if we get to this point, we should be exiting gracefully
            ExitProgram("<<< SimplySerial session terminated >>>", exitCode: 0);
        }
Example #3
0
        static void Main(string[] args)
        {
            // attempt to enable virtual terminal escape sequence processing
            try
            {
                var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
                GetConsoleMode(iStdOut, out uint outConsoleMode);
                outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
                SetConsoleMode(iStdOut, outConsoleMode);
            }
            catch
            {
                // if the above fails, it doesn't really matter - it just means escape sequences won't process nicely
            }

            // load and parse data in boards.json
            LoadBoards();

            // process all command-line arguments
            ProcessArguments(args);

            // verify log-related settings
            if (logging)
            {
                try
                {
                    FileStream stream = new FileStream(logFile, logMode, FileAccess.Write);
                    using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))
                    {
                        writer.WriteLine($"\n----- LOGGING STARTED ({DateTime.Now}) ------------------------------------");
                    }
                }
                catch (Exception e)
                {
                    logging = false;
                    ExitProgram($"* Error accessing log file '{logFile}'\n  > {e.GetType()}: {e.Message}", exitCode: -1);
                }
            }

            // set up keyboard input for program control / relay to serial port
            ConsoleKeyInfo keyInfo = new ConsoleKeyInfo();

            Console.TreatControlCAsInput = true; // we need to use CTRL-C to activate the REPL in CircuitPython, so it can't be used to exit the application

            // this is where data read from the serial port will be temporarily stored
            string received = string.Empty;

            //main loop - keep this up until user presses CTRL-X or an exception takes us down
            do
            {
                // first things first, check for (and respect) a request to exit the program via CTRL-X
                if (Console.KeyAvailable)
                {
                    keyInfo = Console.ReadKey(intercept: true);
                    if ((keyInfo.Key == ConsoleKey.X) && (keyInfo.Modifiers == ConsoleModifiers.Control))
                    {
                        Output("\n<<< SimplySerial session terminated via CTRL-X >>>");
                        ExitProgram(silent: true);
                    }
                }

                // get a list of available ports
                availablePorts = (SimplySerial.GetSerialPorts()).OrderBy(p => p.num).ToList();

                // if no port was specified/selected, pick one automatically
                if (port.name == String.Empty)
                {
                    // if there are com ports available, pick one
                    if (availablePorts.Count() >= 1)
                    {
                        // first, try to default to something that we assume is running CircuitPython
                        SimplySerial.port = availablePorts.Find(p => p.isCircuitPython == true);

                        // if that doesn't work out, just default to the first available COM port
                        if (SimplySerial.port == null)
                        {
                            SimplySerial.port = availablePorts[0];
                        }
                    }

                    // if there are no com ports available, exit or try again depending on autoconnect setting
                    else
                    {
                        if (autoConnect == AutoConnect.NONE)
                        {
                            ExitProgram("No COM ports detected.", exitCode: -1);
                        }
                        else
                        {
                            continue;
                        }
                    }
                }

                // if a specific port has been selected, try to match it with one that actually exists
                else
                {
                    bool portMatched = false;

                    foreach (ComPort p in availablePorts)
                    {
                        if (p.name == port.name)
                        {
                            portMatched = true;
                            port        = p;
                            break;
                        }
                    }

                    // if the specified port is not available, exit or try again depending on autoconnect setting
                    if (!portMatched)
                    {
                        if (autoConnect == AutoConnect.NONE)
                        {
                            ExitProgram(("Invalid port specified <" + port.name + ">"), exitCode: -1);
                        }
                        else
                        {
                            continue;
                        }
                    }
                }

                // if we get this far, it should be safe to set up the specified/selected serial port
                serialPort = new SerialPort(port.name)
                {
                    Handshake    = Handshake.None, // we don't need to support any handshaking at this point
                    ReadTimeout  = 1,              // minimal timeout - we don't want to wait forever for data that may not be coming!
                    WriteTimeout = 250,            // small delay - if we go too small on this it causes System.IO semaphore timeout exceptions
                    DtrEnable    = true,           // without this we don't ever receive any data
                    RtsEnable    = true            // without this we don't ever receive any data
                };

                // attempt to set the baud rate, fail if the specified value is not supported by the hardware
                try
                {
                    if (baud < 0)
                    {
                        if (port.isCircuitPython)
                        {
                            baud = 115200;
                        }
                        else
                        {
                            baud = 9600;
                        }
                    }

                    serialPort.BaudRate = baud;
                }
                catch (ArgumentOutOfRangeException)
                {
                    ExitProgram(("The specified baud rate (" + baud + ") is not supported."), exitCode: -2);
                }

                // set other port parameters (which have already been validated)
                serialPort.Parity   = parity;
                serialPort.DataBits = dataBits;
                serialPort.StopBits = stopBits;

                // attempt to open the serial port, deal with failures
                try
                {
                    serialPort.Open();
                }
                catch (Exception e)
                {
                    // if auto-connect is disabled than any exception should result in program termination
                    if (autoConnect == AutoConnect.NONE)
                    {
                        if (e is UnauthorizedAccessException)
                        {
                            ExitProgram((e.GetType() + " occurred while attempting to open " + port.name + ".  Is this port already in use in another application?"), exitCode: -1);
                        }
                        else
                        {
                            ExitProgram((e.GetType() + " occurred while attempting to open " + port.name + "."), exitCode: -1);
                        }
                    }

                    // if auto-connect is enabled, prepare to try again
                    serialPort.Dispose();
                    Thread.Sleep(1000); // putting a delay here to avoid gobbling tons of resources thruogh constant high-speed re-connect attempts
                    continue;
                }

                // if we get this far, clear the screen and send the connection message if not in 'quiet' mode
                Console.Clear();
                Output(String.Format("<<< SimplySerial v{0} connected via {1} >>>\n" +
                                     "Settings  : {2} baud, {3} parity, {4} data bits, {5} stop bit{6}, auto-connect {7}.\n" +
                                     "Device    : {8} {9}{10}\n{11}" +
                                     "---\n\nUse CTRL-X to exit.\n",
                                     version,
                                     port.name,
                                     baud,
                                     (parity == Parity.None) ? "no" : (parity.ToString()).ToLower(),
                                     dataBits,
                                     (stopBits == StopBits.None) ? "0" : (stopBits == StopBits.One) ? "1" : (stopBits == StopBits.OnePointFive) ? "1.5" : "2", (stopBits == StopBits.One) ? "" : "s",
                                     (autoConnect == AutoConnect.ONE) ? "on" : (autoConnect == AutoConnect.ANY) ? "any" : "off",
                                     port.board.make,
                                     port.board.model,
                                     (port.isCircuitPython) ? " (CircuitPython-capable)" : "",
                                     (logging == true) ? ($"Logfile   : {logFile} (Mode = " + ((logMode == FileMode.Create) ? "OVERWRITE" : "APPEND") + ")\n") : ""
                                     ), flush: true);

                lastFlush = DateTime.Now;
                DateTime start          = DateTime.Now;
                TimeSpan timeSinceRX    = new TimeSpan();
                TimeSpan timeSinceFlush = new TimeSpan();

                // this is the core functionality - loop while the serial port is open
                while (serialPort.IsOpen)
                {
                    try
                    {
                        // process keypresses for transmission through the serial port
                        if (Console.KeyAvailable)
                        {
                            // determine what key is pressed (including modifiers)
                            keyInfo = Console.ReadKey(intercept: true);

                            // exit the program if CTRL-X was pressed
                            if ((keyInfo.Key == ConsoleKey.X) && (keyInfo.Modifiers == ConsoleModifiers.Control))
                            {
                                Output("\n<<< SimplySerial session terminated via CTRL-X >>>");
                                ExitProgram(silent: true);
                            }

                            // check for keys that require special processing (cursor keys, etc.)
                            else if (specialKeys.ContainsKey(keyInfo.Key))
                            {
                                serialPort.Write(specialKeys[keyInfo.Key]);
                            }

                            // everything else just gets sent right on through
                            else
                            {
                                serialPort.Write(Convert.ToString(keyInfo.KeyChar));
                            }
                        }

                        // process data coming in from the serial port
                        received = serialPort.ReadExisting();

                        // if anything was received, process it
                        if (received.Length > 0)
                        {
                            if (forceNewline)
                            {
                                received = received.Replace("\r", "\n");
                            }

                            // write what was received to console
                            Output(received, force: true, newline: false);
                            start = DateTime.Now;
                        }
                        else
                        {
                            Thread.Sleep(1);
                        }

                        if (logging)
                        {
                            timeSinceRX    = DateTime.Now - start;
                            timeSinceFlush = DateTime.Now - lastFlush;
                            if ((timeSinceRX.TotalSeconds >= 2) || (timeSinceFlush.TotalSeconds >= 10))
                            {
                                if (logData.Length > 0)
                                {
                                    Output("", force: true, newline: false, flush: true);
                                }
                                start     = DateTime.Now;
                                lastFlush = DateTime.Now;
                            }
                        }

                        // if the serial port is unexpectedly closed, throw an exception
                        if (!serialPort.IsOpen)
                        {
                            throw new IOException();
                        }
                    }
                    catch (Exception e)
                    {
                        if (autoConnect == AutoConnect.NONE)
                        {
                            ExitProgram((e.GetType() + " occurred while attempting to read/write to/from " + port.name + "."), exitCode: -1);
                        }
                        else
                        {
                            Output("\n<<< Communications Interrupted >>>\n");
                        }
                        try
                        {
                            serialPort.Dispose();
                        }
                        catch
                        {
                            //nothing to do here, other than prevent execution from stopping if dispose() throws an exception
                        }
                        Thread.Sleep(2000); // sort-of arbitrary delay - should be long enough to read the "interrupted" message
                        if (autoConnect == AutoConnect.ANY)
                        {
                            port.name = String.Empty;
                            Output("<<< Attemping to connect to any available COM port.  Use CTRL-X to cancel >>>");
                        }
                        else if (autoConnect == AutoConnect.ONE)
                        {
                            Output("<<< Attempting to re-connect to " + port.name + ". Use CTRL-X to cancel >>>");
                        }
                        break;
                    }
                }
            } while (autoConnect > AutoConnect.NONE);

            // if we get to this point, we should be exiting gracefully
            ExitProgram("<<< SimplySerial session terminated >>>", exitCode: 0);
        }