28

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

是否有可能:

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

8 回答 8

24

这段代码的表现非常好(如果检测到 Arduino,则返回 COM 端口字符串,即“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;
        }
于 2011-03-22T20:55:26.173 回答
11
  1. 您可以使用SerialPort.GetPortNames()返回字符串 COM 端口名称的数组。
  2. 我认为您不能自动检测端口,您必须 ping 设备才能查看设备是否已连接。
于 2010-07-20T20:08:48.147 回答
5

将 WMI 管理路线再进一步,我提出了一个包装类,它连接到 Win32_SerialPorts 事件并动态填充 Arduino 和 Digi International (X-Bee) 设备的 SerialPorts 列表,包括 PortNames 和 BaudRates。

目前,我使用 Win32_SerialPorts 条目中的设备描述字段作为字典的键,但这很容易更改。

它已经用 Arduino UNO 进行了有限容量的测试,它似乎很稳定。

// -------------------------------------------------------------------------
//  <copyright file="ArduinoDeviceManager.cs" company="ApacheTech Consultancy">
//      Copyright (c) ApacheTech Consultancy. All rights reserved.
//  </copyright>
//  <license type="GNU General Public License" version="3">
//      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
//  <license>
// -------------------------------------------------------------------------

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
{
    /// <summary>
    ///     Provides automated detection and initiation of Arduino devices. This class cannot be inherited.
    /// </summary>
    public sealed class ArduinoDeviceManager : IDisposable, INotifyPropertyChanged
    {
        /// <summary>
        ///     A System Watcher to hook events from the WMI tree.
        /// </summary>
        private readonly ManagementEventWatcher _deviceWatcher = new ManagementEventWatcher(new WqlEventQuery(
            "SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2 OR EventType = 3"));

        /// <summary>
        ///     A list of all dynamically found SerialPorts.
        /// </summary>
        private Dictionary<string, SerialPort> _serialPorts = new Dictionary<string, SerialPort>();

        /// <summary>
        ///     Initialises a new instance of the <see cref="ArduinoDeviceManager"/> class.
        /// </summary>
        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();
        }

        /// <summary>
        ///     Gets a list of all dynamically found SerialPorts.
        /// </summary>
        /// <value>A list of all dynamically found SerialPorts.</value>
        public Dictionary<string, SerialPort> SerialPorts
        {
            get { return _serialPorts; }
            private set
            {
                _serialPorts = value;
                OnPropertyChanged();
            }
        }

        /// <summary>
        ///     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            // Stop the WMI monitors when this instance is disposed.
            _deviceWatcher.Stop();
        }

        /// <summary>
        ///     Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        ///     Handles the EventArrived event of the _deviceWatcher control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArrivedEventArgs"/> instance containing the event data.</param>
        private void _deviceWatcher_EventArrived(object sender, EventArrivedEventArgs e)
        {
            DiscoverArduinoDevices();
        }

        /// <summary>
        ///     Dynamically populates the SerialPorts property with relevant devices discovered from the WMI Win32_SerialPorts class.
        /// </summary>
        private void DiscoverArduinoDevices()
        {
            // Create a temporary dictionary to superimpose onto the SerialPorts property.
            var dict = new Dictionary<string, SerialPort>();

            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")) continue; // 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<ManagementObject>().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);
            }
        }

        /// <summary>
        ///     Called when a property is set.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
于 2013-09-29T15:07:40.897 回答
1

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

在 Arduino 代码的设置部分,我让它调用 setupComms() 方法,该方法简单地打印一个“A”,直到它收到一个“a”。一旦收到“a”,它就会跳转到主循环()函数。所以 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 语句都在那里,到目前为止这对我有用。祝你好运!

于 2013-01-03T22:51:21.453 回答
1

我刚刚遇到了一个与基于 PC 的处理语言程序通信的 Teensyduino 类似的挑战。它可能对使用 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.
于 2018-05-31T05:36:38.673 回答
0

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

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

但是,它在执行时正确显示:

foreach (string z in SerialPort.GetPortNames())

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

于 2016-02-13T23:44:29.050 回答
0

此方法不能帮助您找出您的 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();
于 2017-05-15T18:30:19.663 回答
0

使用这个 php 脚本,您可以从 arduino 传输和接收数据

<?php
 /**
 * Remember to go to Device Manager> Ports (COM & LPT)>Arduino XXX (COMXX)>right
 * click>Properties>
 * Port Settings>Advanced>uncheck "use FIFO buffers ........."
 * In other hand, remeber that the Tx speed has to be the same in PhpConnect.php, in
 * Arduino sketch and in the COM
 * properties in Device manager, I selected 115200 b/s.
 *
 */
  // RX form PC**************
 $t = $_POST['text1'];
 include 'PruebaBatchCOM.php';
 $puerto = escapeshellarg($usbCOM);
 $dato = escapeshellarg($t);
 exec("node C:\\xampp\\htdocs\\DisenoWEBTerminados\\BatteryTester\\Scripts\\writeandread.js {$puerto} {$dato} 2>&1", $output1);
 $str = implode($output1);
 $str1 = explode(",",$str);
 $myJSON = json_encode($str1);// this is the response to AJAX
 echo $myJSON;
 ?>

PruebaBatchCOM.php 是

<?php
$puerto = array(); 
$file111 = "PruebaCOMRetrieve.bat";
exec($file111, $puerto);
$usbCOM = implode(",",$puerto); 
?>

PruebaCOMRetrieve.bat

@echo off
setlocal

for /f "tokens=1* delims==" %%I in ('wmic path win32_pnpentity get caption  
/format:list ^| find "Arduino Uno"') do (
call :setCOM "%%~J"
)

:: end main batch
goto :EOF

:setCOM <WMIC_output_line>
:: sets _COM#=line
setlocal
set "str=%~1"
set "num=%str:*(COM=%"
set "num=%num:)=%"
set port=COM%num%
echo %port%
于 2020-10-09T21:58:41.097 回答