使用C# Mono(树莓派)无法从串口读取数据

5
我正在尝试编写一个 C# 库,以查看 Raspberry Pi 上所有可用的 USB 串口,以便我可以枚举、识别和与连接到 Pi 的一组 Arduino 通过 USB 集线器进行通信。
我可以在我的 Windows 机器上(几个 Arduino 连接到我的台式电脑)使它工作,甚至也能让它在我的 Pi 上工作,但是,我不知道如何推广这种解决方法。
如果我试图在 Pi 上单独运行程序,则可以打开串口并发送数据,但是我无法从 Arduino 接收任何内容:我会得到超时异常。我知道 Mono 的 SerialPort 实现有限,我必须使用 SerialPort.ReadByte() 而不是 Readline() 和数据接收事件(我的解决方案基于来自 HowToSystemIOPorts 的代码)。我的串口枚举使用另一个堆栈交换响应中概述的方法 here
我的超时目前设置为 4 秒,比我期望收到消息的时间长几个数量级。
经过大量搜索,我发现可以使用 minicom 初始化串口 here,令我惊讶的是,这允许我从 Arduino 接收数据。最大的缺点是需要使用 minicom 初始化端口并在每次启动 Pi 时保持该进程打开状态。我还似乎无法弄清楚如何使其与多个 Arduino 一起使用。

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

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

这是我希望解决的问题:

  • 自动设置/初始化 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://dev59.com/a0bRa4cB1Zd3GeqPyDWy
    /// </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();
    }
2个回答

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

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


谢谢,实际上我在代码中使用了IASync方法来读取字节,并设置了无限超时时间(不包括在我的示例中),在识别和连接到Arduino之后。结果发现,在打开串口后,我需要等待Arduino的引导加载程序初始化我的草图。 - GeraldO

1

2
谢谢,这让我找到了问题真正的本质!事实证明,我根本不需要手动配置TTY端口,但是使用stty命令让我发现我的真正问题是打开串行端口会重置Arduino。在尝试与其通信之前,我需要等待引导加载程序启动Arduino。问题解决了! - GeraldO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接