我正在尝试编写一个 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();
}