5

我正在尝试编写一个 C# 库,该库查看 Raspberry Pi 上所有可用的 USB 串行端口,以便我可以枚举、识别一组通过 USB 集线器连接到 Pi 的 Arduino 并与之通信。

我能够在我的 Windows 机器上完成这项工作(几个 Arduino 连接到我的台式计算机),甚至能够在我的 Pi 上工作,但是,我很难理解如何概括修复。

如果我尝试在 Pi 上自行运行程序,我可以打开串行端口并发送数据,但是,我无法从 Arduino 接收到任何信息:我收到超时异常。我知道 Mono 的 SerialPort 实现是有限的,我必须使用 SerialPort.ReadByte() 而不是 Readline() 和接收到的数据事件(我的解决方案基于HowToSystemIOPorts的代码)。我的串行端口枚举正在使用此处另一个堆栈交换响应中概述的方法。

我的超时当前设置为 4 秒,这比我预期接收消息的时间长了几个数量级。

经过大量的谷歌搜索,我在这里提到了使用 minicom 初始化串行端口,令我惊讶的是,这让我能够从 Arduino 接收数据。最大的缺点是我需要使用 minicom 初始化端口,并在每次启动 Pi 时保持进程打开。我似乎也无法弄清楚如何使用多个 Arduino 进行这项工作。


这是我到目前为止所尝试的:

  • 将 Pi 固件和软件更新到最新版本
  • 尝试同时使用 Arduino MEGA 2560 R3 和 Arduino UNO
  • 将 tty* 端口(在本例中为 ttyACM0 和 ttyUSB0)的所有者更改为我的用户和组
  • 通过 minicom 成功配置端口,让进程运行并启动程序并读取/写入数据。一次似乎只适用于一个 Arduino 的手动过程
  • 在 Windows 中成功运行程序而无故障
  • 验证 Arduino 被运行“dmesg | grep tty”的 Pi 识别

这是我希望解决的问题:

  • Arduino 串行端口的自动设置/初始化。无论是通过在主程序之前运行的 shell 脚本还是在 Mono 代码中运行,以便下面的代码可以按预期运行。

这是我的连接代码:

    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();
    }
4

2 回答 2

1

看看 stty 命令。它将让您设置/读取终端设置

http://linux.about.com/od/lna_guide/a/gdelna38t01.htm将简要介绍它的使用。它比 minicom 更容易调用,并且设置保留在设备上。

于 2013-05-06T00:13:35.150 回答
1

我以前做过和你一样的事情。我必须通过 USB 串行适配器读取和写入数据,并且没有使用 minicom。它可能不是上帝的代码,但我发现为了读取数据,我可以创建一个新线程并检查数据,我的代码包含很多东西,但基本上我是这样做的:

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

和 check_get_data 方法

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

这目前正在使用两个 usbserials 运行。不知道它是否有帮助,但希望你找到你的解决方案

于 2013-05-06T12:12:46.460 回答