簡體   English   中英

如何自動檢測Arduino COM端口?

[英]How to auto-detect Arduino COM port?

我正在使用帶有 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) 設備的 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 語句在我最初測試時都在那里,到目前為止這對我有用。 祝你好運!

我剛剛遇到了一個類似的挑戰,讓 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.

我注意到我的 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%

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM