我正在使用带有 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%