我正在使用带有 Firmata 库的 Arduino 与 C# 应用程序进行通信,并且我想消除 COM 端口配置组件,因为它可以在机器之间更改......
是否有可能:
- 枚举系统中的 COM 端口列表?(在我的谷歌搜索中,我看到了一些相当丑陋的 Win32 API 代码,希望现在可能有一个更干净的版本)
- 自动检测哪些 COM 端口连接到 Arduino?
这段代码的表现非常好(如果检测到 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;
        }
将 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));
        }
    }
}
试试这个,我正在做一个非常相似的项目,任何人都请随时编辑这个!
在 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 语句都在那里,到目前为止这对我有用。祝你好运!
我刚刚遇到了一个与基于 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.
我注意到我的 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"));
此方法不能帮助您找出您的 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();
使用这个 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%