Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to read from Serial Port using C# Mono (RaspberryPi)

I'm attempting to write a C# library which looks at all available USB serial ports on a Raspberry Pi so that I can enumerate, identify and communicate with a set of Arduinos connected to the Pi via a USB hub.

I am able to make this work on my windows machine (several Arduinos connected to my desktop computer) and have even been able to make it work on my Pi however, I am struggling to understand how to generalize the fix.

If I attempt to run the program by itself on the Pi, I am able to open the serial port and send data however, I cannot receive anything from the Arduinos: I get timeout exceptions. I understand that Mono's implementation of SerialPort is limited and I must use SerialPort.ReadByte() instead of Readline() and the data received events (my solution is based on code from HowToSystemIOPorts). My Serial port enumeration is using a method outlined in another stack exchange response here.

My timeout is currently set to 4 seconds, which is several orders of magnitude longer than I expect to receive the message.

After a lot of googling, I came across mention of using minicom to initialize the serial port here, which to my surprise allowed me to receive data from the Arduino. The biggest drawback is that I need to initialize the port using minicom and leave the process opening each time I boot the Pi. I also can't seem to figure out how to make this work with multiple Arduinos.


Here is what I have tried so far:

  • Updated the Pi firmware and software to their latest versions
  • Attempted to use both an Arduino MEGA 2560 R3 and Arduino UNO
  • Changed the owner of the tty* ports (ttyACM0 and ttyUSB0 in this case) to both my user and group
  • Successfully configured the port via minicom, left the process running and start the program and read/wrote data. A manual process which only seems to work for one Arduino at a time
  • Successfully run the program in Windows without fault
  • Verified the Arduinos are recognized by the Pi running "dmesg | grep tty"

Here is what I hope to solve:

  • Automatic setup/initialization of the Arduino serial ports. Whether through a shell script run before the main program or within Mono code so that the code below can run as intended.

Here is my connection code:

    public bool StartArduinoComms()
    {
        string[] ports = GetPortNames();
        foreach (string port in ports)
        {
            mLogger.LogMessage(ProsthesisCore.Utility.Logger.LoggerChannels.Arduino, string.Format("Found serial port {0}", port));
        }

        bool foundCorrectArduino = false;

        var idPacket = new ArduinoMessageBase();
        idPacket.ID = ArduinoMessageValues.kIdentifyValue;

        string jsonOutput = Newtonsoft.Json.JsonConvert.SerializeObject(idPacket);

        foreach (string port in ports)
        {
            SerialPort serialPort = new SerialPort(port, kArduinoCommsBaudRate);
            serialPort.Parity = Parity.None;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;

            //Only check unopened ports
            if (!serialPort.IsOpen)
            {
                serialPort.Open();

                //Disable telemtry just incase
                var toggle = new { ID = ArduinoMessageValues.kTelemetryEnableValue, EN = false };
                string disableTelem = Newtonsoft.Json.JsonConvert.SerializeObject(toggle);
                serialPort.Write(disableTelem);

                //Discard any built up data
                serialPort.DiscardInBuffer();
                serialPort.Write(jsonOutput);
                serialPort.ReadTimeout = kIDTimeoutMilliseconds;

                string response = string.Empty;
                for (int i = 0; i < kNumRetries; ++i)
                {
                    try
                    {
                        //This is guaranteed to timeout if not configured through minicom
                        response = ReadLine(serialPort);
                        break;
                    }
                    //Catch case where the serial port is unavailable. MOve to next port
                    catch (TimeoutException)
                    {
                        continue;
                    }
                }

                if (!string.IsNullOrEmpty(response))
                {
                    //Perform response validation
                }
                else
                {
                    //Got no response
                }

                if (!foundCorrectArduino)
                {
                    serialPort.Close();
                }
            }
        }

        return foundCorrectArduino;
    }
    /// <summary>
    /// From https://stackoverflow.com/questions/434494/serial-port-rs232-in-mono-for-multiple-platforms
    /// </summary>
    /// <returns></returns>
    private static string[] GetPortNames()
    {
        int p = (int)Environment.OSVersion.Platform;
        List<string> serial_ports = new List<string>();

        // Are we on Unix?
        if (p == 4 || p == 128 || p == 6)
        {
            string[] ttys = System.IO.Directory.GetFiles("/dev/", "tty*");
            foreach (string dev in ttys)
            {
                //Arduino MEGAs show up as ttyACM due to their different USB<->RS232 chips
                if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB") || dev.StartsWith("/dev/ttyACM"))
                {
                    serial_ports.Add(dev);
                }
            }
        }
        else
        {
            serial_ports.AddRange(SerialPort.GetPortNames());
        }

        return serial_ports.ToArray();
    }
like image 827
GeraldO Avatar asked Nov 03 '22 22:11

GeraldO


2 Answers

Have a look at stty command. It will let you set/read teminal settings

http://linux.about.com/od/lna_guide/a/gdelna38t01.htm will give a rundown on it's use. It would be easier to call out to than minicom, and the settings stay on the device.

like image 55
Tim Hoffman Avatar answered Nov 08 '22 03:11

Tim Hoffman


I have done something like the same as you before. I had to read and write data through USB Serial adapter, and didnt use minicom. It may not be god code but i found that inorder to read the data I could create a new thread and have that check for data, my code include a lot of stuff but basicly i did this:

System.Threading.Thread newThread;
newThread = new System.Threading.Thread(this.check_get_data);

and the check_get_data method

public void check_get_data ()
    {
        byte tmpByte = 0;
        while (m_objSerialPort.BytesToRead != 0) {
            tmpByte = (byte)m_objSerialPort.ReadByte ();
            DoSomethingWithByte(tmpByte);
            Thread.Sleep(20);
        }
    }

this is currently running with two usbserials. dont know if it helps but hope you find your solution

like image 44
IYH Avatar answered Nov 08 '22 04:11

IYH