如何自动检测Arduino COM端口?

我正在使用带有Firmata库的Arduino与C#应用程序进行通信,我想要消除COM端口配置组件,因为它可以在不同的机器之间进行更改…

是否有可能:

  1. 枚举系统中的COM端口列表? (在我的谷歌搜索中,我看到了一些相当丑陋的Win32 API代码,希望现在可能有更清洁的版本)
  2. 自动检测哪个COM端口连接到Arduino?

这一小段代码对此表现非常好(返回COM端口字符串,如果检测到Arduino则返回“COM12”):

private string AutodetectArduinoPort() { ManagementScope connectionScope = new ManagementScope(); SelectQuery serialQuery = new SelectQuery("SELECT * FROM Win32_SerialPort"); ManagementObjectSearcher searcher = new ManagementObjectSearcher(connectionScope, serialQuery); try { foreach (ManagementObject item in searcher.Get()) { string desc = item["Description"].ToString(); string deviceId = item["DeviceID"].ToString(); if (desc.Contains("Arduino")) { return deviceId; } } } catch (ManagementException e) { /* Do Nothing */ } return null; } 
  1. 您可以使用SerialPort.GetPortNames()返回字符串COM端口名称的数组。
  2. 我不认为你可以自动检测端口,你必须ping设备,以查看设备是否已连接。

进一步采用WMI管理路由,我想出了一个包装类,它挂接到Win32_SerialPorts事件,并动态填充了Arduino和Digi International(X-Bee)设备的SerialPort列表,包括PortNames和BaudRates。

现在,我已经使用Win32_SerialPorts条目中的设备描述字段作为字典的键,但这很容易改变。

它已经过Arduino UNO的有限容量测试,看起来很稳定。

 // ------------------------------------------------------------------------- //  // Copyright (c) ApacheTech Consultancy. All rights reserved. //  //  // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses //  // ------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO.Ports; using System.Linq; using System.Management; using System.Runtime.CompilerServices; // Automatically imported by Jetbeans Resharper using ArduinoLibrary.Annotations; namespace ArduinoLibrary { ///  /// Provides automated detection and initiation of Arduino devices. This class cannot be inherited. ///  public sealed class ArduinoDeviceManager : IDisposable, INotifyPropertyChanged { ///  /// A System Watcher to hook events from the WMI tree. ///  private readonly ManagementEventWatcher _deviceWatcher = new ManagementEventWatcher(new WqlEventQuery( "SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2 OR EventType = 3")); ///  /// A list of all dynamically found SerialPorts. ///  private Dictionary _serialPorts = new Dictionary(); ///  /// Initialises a new instance of the  class. ///  public ArduinoDeviceManager() { // Attach an event listener to the device watcher. _deviceWatcher.EventArrived += _deviceWatcher_EventArrived; // Start monitoring the WMI tree for changes in SerialPort devices. _deviceWatcher.Start(); // Initially populate the devices list. DiscoverArduinoDevices(); } ///  /// Gets a list of all dynamically found SerialPorts. ///  /// A list of all dynamically found SerialPorts. public Dictionary SerialPorts { get { return _serialPorts; } private set { _serialPorts = value; OnPropertyChanged(); } } ///  /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. ///  public void Dispose() { // Stop the WMI monitors when this instance is disposed. _deviceWatcher.Stop(); } ///  /// Occurs when a property value changes. ///  public event PropertyChangedEventHandler PropertyChanged; ///  /// Handles the EventArrived event of the _deviceWatcher control. ///  /// The source of the event. /// The  instance containing the event data. private void _deviceWatcher_EventArrived(object sender, EventArrivedEventArgs e) { DiscoverArduinoDevices(); } ///  /// Dynamically populates the SerialPorts property with relevant devices discovered from the WMI Win32_SerialPorts class. ///  private void DiscoverArduinoDevices() { // Create a temporary dictionary to superimpose onto the SerialPorts property. var dict = new Dictionary(); try { // Scan through each SerialPort registered in the WMI. foreach (ManagementObject device in new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_SerialPort").Get()) { // Ignore all devices that do not have a relevant VendorID. if (!device["PNPDeviceID"].ToString().Contains("VID_2341") && // Arduino !device["PNPDeviceID"].ToString().Contains("VID_04d0")) return; // Digi International (X-Bee) // Create a SerialPort to add to the collection. var port = new SerialPort(); // Gather related configuration details for the Arduino Device. var config = device.GetRelated("Win32_SerialPortConfiguration") .Cast().ToList().FirstOrDefault(); // Set the SerialPort's PortName property. port.PortName = device["DeviceID"].ToString(); // Set the SerialPort's BaudRate property. Use the devices maximum BaudRate as a fallback. port.BaudRate = (config != null) ? int.Parse(config["BaudRate"].ToString()) : int.Parse(device["MaxBaudRate"].ToString()); // Add the SerialPort to the dictionary. Key = Arduino device description. dict.Add(device["Description"].ToString(), port); } // Return the dictionary. SerialPorts = dict; } catch (ManagementException mex) { // Send a message to debug. Debug.WriteLine(@"An error occurred while querying for WMI data: " + mex.Message); } } ///  /// Called when a property is set. ///  /// Name of the property. [NotifyPropertyChangedInvocator] private void OnPropertyChanged([CallerMemberName] string propertyName = null) { var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } } } 

试试这个,我正在做一个非常相似的项目,也有人请随时编辑这个!

在Arduino代码的设置部分,我让它调用一个setupComms()方法,这个方法只是打印一个“A”,直到它收到一个“a”。 一旦收到“a”,它就会跳转到main loop()函数。 所以C#部分检查每个可用端口是否为“A”,如果找到“A”,我们就知道我们已经打开了Arduino的端口!

再次,这可能不是很干净但它确实有效,我对任何意见和建议持开放态度!

  foreach (string s in SerialPort.GetPortNames()) { com.Close(); // To handle the exception, in case the port isn't found and then they try again... bool portfound = false; com.PortName = s; com.BaudRate = 38400; try { com.Open(); status.Clear(); status.Text += "Trying port: " + s+"\r"; } catch (IOException c) { status.Clear(); status.Text += "Invalid Port"+"\r"; return; } catch (InvalidOperationException c1) { status.Clear(); status.Text += "Invalid Port" + "\r"; return; } catch (ArgumentNullException c2) { // System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c2); status.Clear(); status.Text += "Invalid Port" + "\r"; return; } catch (TimeoutException c3) { // System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c3); status.Clear(); status.Text += "Invalid Port" + "\r"; return; } catch (UnauthorizedAccessException c4) { //System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c); status.Clear(); status.Text += "Invalid Port" + "\r"; return; } catch (ArgumentOutOfRangeException c5) { //System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c5); status.Clear(); status.Text += "Invalid Port" + "\r"; return; } catch (ArgumentException c2) { //System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c2); status.Clear(); status.Text += "Invalid Port" + "\r"; return; } if (!portfound) { if (com.IsOpen) // Port has been opened properly... { com.ReadTimeout = 500; // 500 millisecond timeout... sent.Text += "Attemption to open port " + com.PortName + "\r"; try { sent.Text += "Waiting for a response from controller: " + com.PortName + "\r"; string comms = com.ReadLine(); sent.Text += "Reading From Port " + com.PortName+"\r"; if (comms.Substring(0,1) == "A") // We have found the arduino! { status.Clear(); status.Text += s + com.PortName+" Opened Successfully!" + "\r"; //com.Write("a"); // Sends 0x74 to the arduino letting it know that we are connected! com.ReadTimeout = 200; com.Write("a"); sent.Text += "Port " + com.PortName + " Opened Successfully!"+"\r"; brbox.Text += com.BaudRate; comboBox1.Text = com.PortName; } else { sent.Text += "Port Not Found! Please cycle controller power and try again" + "\r"; com.Close(); } } catch (Exception e1) { status.Clear(); status.Text += "Incorrect Port! Trying again..."; com.Close(); } } } } 

从我最初测试开始,所有的Try Catch语句都在那里,到目前为止这对我有用。 祝好运!

此方法无法帮助您找出arduino连接到计算机的端口

SerialPort.GetPortNames方法()

 // Get a list of serial port names. string[] ports = SerialPort.GetPortNames(); Console.WriteLine("The following serial ports were found:"); Console.WriteLine("Aşşağıda Seri Bağlantı Noktaları Bulundu:");//For Turkish // Display each port name to the console. foreach(string port in ports) { Console.WriteLine(port); } Console.ReadLine(); 

我注意到我的Arduino nano中文克隆在设备管理器中正确显示了COM端口,但是当您尝试使用此命令获取所有端口时,它不显示在C#app dorp下拉列表中:

 using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort")); 

但是,执行时会正确显示:

 foreach (string z in SerialPort.GetPortNames()) 

所以我有2个列表:一个输出第一个代码,另一个输出第二个代码。 比较两者时,它会找到正确的COM端口。 显然,当使用原始Andurino Uno时,两个命令都正确显示端口,因此该解决方案仅适用于中文克隆,由于某些奇怪的原因,使用它们是不可见的using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort"));

我刚刚与Teensyduino进行了类似的挑战,与基于PC的处理语言程序进行通信。 对于使用Java或处理语言而不是C#的人来说,这可能很有用。

此解决方案的基本思想是向每个串行端口发送握手请求(“!sh \ n”),然后从每个设备侦听响应(“$ h \ n”),直到收到正确的握手响应。 从而显示哪个端口是我正在寻找的设备。

另外,我是StackOverflow的新手,所以如果我在这个答案中打破任何StackOverflow礼仪,请原谅并教育我。

处理语言代码:

 import processing.serial.*; int ellipticalWalkerTeensyIndex; /* Represents Elliptical Walker Serial Port index in the Serial.list() String array. */ boolean latch; String[] serialPortList = Serial.list(); int serialPortCount = serialPortList.length; Serial[] ports = new Serial[serialPortCount]; int[] serialConnected = new int[serialPortCount]; void setup(){ for (int z = 0; z < serialPortCount; ++z) { /* Initialise serialConnected array to 0; Anything not marked to 1 later will be ignored. */ serialConnected[z] = 0; } ellipticalWalkerTeensyIndex = -1; /* Initialise ellipticalWalkerTeensyIndex to -1, as the correct serial port is not yet known. */ latch = false; for (int z = 0; z < serialPortCount; ++z) { try { ports[z] = new Serial(this, serialPortList[z], 9600); serialConnected[z] = 1; /* Mark this index as connected. */ ports[z].write("!sh"); /* Send handshake request; Expected response is "$h\n" */ }catch (Exception e){ println("Could not connect to "+Integer.toString(z)+" exception details: "+e); } } } void draw(){ if (ellipticalWalkerTeensyIndex < 0) { for (int z = 0; z < serialPortCount; ++z) { if(serialConnected[z]>0){ /* Only attempt communication if we have marked this serial port as connected during the setup routine. */ if (ports[z].available()>0) { /* Read from serial port 'z' if data is available. */ String lineOfData = ports[z].readStringUntil('\n'); if(lineOfData.charAt(0)=='$' && lineOfData.charAt(1)=='h'){ /* Check if received response matches expected handshake response */ ellipticalWalkerTeensyIndex = z; /* Note the correct serial port for the teensy. */ } } } } }else{ if (!latch) { println("The teensyduino is on serial port: "+serialPortList[ellipticalWalkerTeensyIndex]); latch = true; exit(); } } } 

运行时结果:

 PS C:\repos\elliptical_walker> processing-java --sketch=c:\repos\elliptical_walker\EW0 --run The teensyduino is on serial port: COM3 Finished.