web-dev-qa-db-ja.com

Arduino COMポートを自動検出する方法は?

C#アプリケーションとの通信にFirmataライブラリを備えたArduinoを使用していますが、COMポート構成コンポーネントはマシン間で変更される可能性があるため、削除します...

次のことが可能ですか?

  1. システムのCOMポートの一覧を列挙しますか? (私のグーグルでかなり醜いWin32 APIコードを見たことがありますが、今はよりクリーンなバージョンがあることを期待しています)
  2. Arduinoに接続されているCOMポートを自動検出しますか?
22
Brandon

この小さなコードは非常にうまく機能しています(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;
        }
21
Brandon
  1. SerialPort.GetPortNames() を使用して、COMポート名の文字列の配列を返すことができます。
  2. ポートを自動検出できるとは思いません。デバイスが接続されているかどうかを確認するには、デバイスにpingを実行する必要があります。
10
SwDevMan81

WMI管理ルートを少し進めて、Win32_SerialPortsイベントにフックし、ArduinoおよびDigi International(X-Bee)デバイスのシリアルポートのリストに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));
        }
    }
}
5
Apache

これを試してみてください、私は非常に類似したプロジェクトに取り組んでいます。また、誰でも自由に編集してください!

Arduinoコードのセットアップ部分では、setupComms()メソッドを呼び出しています。このメソッドは、「a」を受け取るまで「A」を出力します。 「a」を受信すると、メインの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ステートメントは、私が最初にテストしていたときからありますが、これまでのところ、これでうまくいきました。幸運を!

1
Bubo

私は、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.
1
John Forbes

この方法では、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();
0
Habib Göker

Arduino nanoの中国のクローンがデバイスマネージャーにCOMポートを正しく表示することに気づきましたが、このコマンドを使用してすべてのポートを取得しようとすると、C#アプリのダウンリストに表示されません。

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

ただし、実行時には正しく表示されます。

_foreach (string z in SerialPort.GetPortNames())
_

したがって、2つのリストがあります。1つは第1コードの出力、もう1つは第2コードの出力です。両方を比較すると、正しいCOMポートが見つかります。明らかに、元のAndurino Unoを使用すると、両方のコマンドがポートを正しく表示するため、このソリューションは、奇妙な理由でusing (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort"));からは見えない中国のクローンでのみ機能します

0
Tom