我使用 USB 串行端口设备测试了以下代码,该设备也可能适用于您的体重秤。一些端口设置是通过下载/安装WinCT (RsCom, RsKey & RsWeight)找到的)。然后,在 A&D WinCT 下的 Windows 开始菜单中,选择 RsCom 或 RsKey。使用RsCom 或RsKey 是检查USB 电缆/连接是否正常工作的简单方法。我在我的 USB 串行设备上同时使用了“RsKey”和“RsCom”,它似乎可以工作。
创建一个 WinForms 项目
VS 2017:
- 打开 Visual Studio
- 展开已安装
- 展开Visual C#
- 点击Windows桌面
- 选择 Windows 窗体应用程序 (.NET Framework)
- 指定项目名称(名称:ReadSerialPort)
- 点击确定
VS 2019:
- 打开 Visual Studio
- 点击无代码继续
- 点击文件
- 选择新建
- 选择项目
- C# Windows 桌面
- 单击Windows 窗体应用程序(.NET Framework)
- 点击下一步
- 指定项目名称(名称:ReadSerialPort)
- 点击创建
注意:从现在开始,VS 2017 和 VS 2019 的流程相同。
添加类:SerialPortDataReceivedEventArgs
注意:这个类将与一个事件一起使用,该事件将从串口设备接收到的数据发送给订阅者。
- 在 VS 菜单上,选择 项目
- 选择添加类(名称:SerialPortDataReceivedEventArgs.cs)
SerialPortDataReceivedEventArgs.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReadSerialPort
{
public delegate void SerialPortDataReceivedEventHandler(object sender, SerialPortDataReceivedEventArgs e);
public class SerialPortDataReceivedEventArgs : System.EventArgs
{
public string Data { get; private set; } = string.Empty;
public SerialPortDataReceivedEventArgs(string data)
{
this.Data = data;
}
}
}
添加对 System.Management 的引用
- 在 VS 菜单中,选择 Project
- 选择添加参考
- 展开程序集
- 检查System.Management
- 点击确定
添加类:ComPorts
- 在 VS 菜单上,选择 项目
- 选择添加类(名称:ComPorts.cs)
ComPorts.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReadSerialPort
{
public class ComPorts
{
public List<ComPortInfo> Ports { get; set; } = new List<ComPortInfo>();
}
public class ComPortInfo
{
public string Name { get; set; }
public string PortName { get; set; }
public ComPortInfo()
{
}
public ComPortInfo(string name, string portName)
{
this.Name = name;
this.PortName = portName;
}
}
}
添加类:HelperSerialPort
- 在 VS 菜单上,选择 项目
- 选择添加类(名称:HelperSerialPort.cs)
HelperSerialPort.cs
//if using .NET 5, install NuGet Package: System.IO.Ports
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Diagnostics;
using System.Management;
namespace ReadSerialPort
{
public enum PortBaudRate : int
{
Baud1200 = 1200,
Baud2400 = 2400,
Baud4800 = 4800,
Baud9600 = 9600,
Baud14400 = 14400,
Baud19200 = 19200,
Baud28800 = 28800,
Baud38400 = 38400
};
public class HelperSerialPort : IDisposable
{
public delegate void SerialPortErrorReceivedEventHandler(object sender, SerialErrorReceivedEventArgs e);
public event SerialPortDataReceivedEventHandler DataReceived;
public event SerialPortErrorReceivedEventHandler ErrorReceived;
private string _dataReceived = string.Empty;
public System.IO.Ports.SerialPort Port { get; private set; }
public HelperSerialPort()
{
//create new instance
Port = new SerialPort();
}
public string Connect(string comPort, PortBaudRate baudRate = PortBaudRate.Baud9600)
{
string portName = string.Empty;
string result = string.Empty;
if (String.IsNullOrEmpty(comPort))
{
System.Windows.Forms.MessageBox.Show("COM port not selected.", "Error - COM Port", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
return "Error: COM port not selected.";
}
try
{
if (Port == null)
{
//create new instance
Port = new SerialPort();
}
if (!Port.IsOpen)
{
Debug.WriteLine("opening port");
//create new instance
Port = new SerialPort(comPort);
//set properties
Port.BaudRate = (int)baudRate;
Port.Handshake = Handshake.None;
Port.Parity = Parity.Even; //Even,None,Odd supported
Port.DataBits = 7;
Port.StopBits = StopBits.One;
Port.ReadTimeout = 200;
Port.WriteTimeout = 50;
Port.DtrEnable = true; //enable Data Terminal Ready
Port.RtsEnable = true; //enable Request to send
//open port
Port.Open();
//subscribe to events
Port.DataReceived += Port_DataReceived;
Port.ErrorReceived += Port_ErrorReceived;
//set value
result = "Connected";
}
else
{
Debug.WriteLine("else");
}
}
catch(Exception ex)
{
result = "Error: (Connect) - " + ex.Message;
}
Debug.WriteLine("result: " + result);
return result;
}
public void Close()
{
Dispose();
}
public void Dispose()
{
if (Port != null)
{
if (Port.IsOpen)
{
Port.Close();
}
//unsubscribe from events
Port.DataReceived -= Port_DataReceived;
Port.ErrorReceived -= Port_ErrorReceived;
Port.Dispose();
Port = null;
}
}
public ComPorts GetComPortInfo()
{
ComPorts comPorts = new ComPorts();
SortedDictionary<string, string> comPortNameDict = new SortedDictionary<string, string>();
SortedDictionary<string, string> portDict = new SortedDictionary<string, string>();
string[] portNames = SerialPort.GetPortNames();
//get USB COM ports
using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_PnPEntity"))
{
ManagementObjectCollection pnpEntityItems = searcher.Get();
if (portNames != null && pnpEntityItems != null)
{
var props = pnpEntityItems.GetEnumerator();
foreach (ManagementBaseObject mbo in pnpEntityItems)
{
if (mbo != null)
{
object nameObj = mbo.GetPropertyValue("Name");
object pnpClassObj = mbo.GetPropertyValue("PNPClass");
if (nameObj != null && pnpClassObj != null)
{
if (pnpClassObj.ToString() == "Ports" && nameObj.ToString().ToLower().Contains("(com"))
{
string name = mbo.GetPropertyValue("Name").ToString().Trim();
//Debug.WriteLine("name: " + name);
string portName = string.Empty;
if (name.Contains("(") && name.Contains(")"))
{
portName = name.Substring(name.IndexOf("(") + 1, name.IndexOf(")") - name.IndexOf("(") - 1);
//Debug.WriteLine("Port Name: '" + portName + "'");
}
if (!portDict.ContainsKey(name))
{
//add to dictionary - ex: Voyager 1450g, COM1
portDict.Add(name, portName);
//add to dictionary - ex: COM1, Voyager 1450g
comPortNameDict.Add(portName, name);
}
}
}
}
}
}
}
//add any ports that aren't USB -- ie: RS-232 (serial) devices
//USB devices are already in the dictionary, so only add devices
//that don't already exist in the dictionary
if (portNames != null && portDict != null && comPortNameDict != null)
{
foreach (string name in portNames)
{
if (!comPortNameDict.ContainsKey(name))
{
//add to dictionary
portDict.Add(name, name);
}
}
}
foreach(KeyValuePair<string, string> kvp in portDict)
{
//add to list
comPorts.Ports.Add(new ComPortInfo(kvp.Key, kvp.Value));
}
return comPorts;
}
private void Port_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
Debug.WriteLine("Error: (sp_ErrorReceived) - " + e.EventType);
if (this.ErrorReceived != null)
{
ErrorReceived(this, e);
}
}
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_dataReceived = Port.ReadExisting();
Debug.WriteLine("_dataReceived: " + _dataReceived);
if (this.DataReceived != null)
{
SerialPortDataReceivedEventArgs eventArgs = new SerialPortDataReceivedEventArgs(_dataReceived);
DataReceived(this, eventArgs);
}
}
public void SerialCmdSend(string data)
{
if (Port.IsOpen)
{
try
{
// Send the binary data out the port
byte[] hexstring = Encoding.ASCII.GetBytes(data);
//write to SerialPort
foreach (byte hexval in hexstring)
{
byte[] _hexval = new byte[] { hexval }; // need to convert byte to byte[] to write
Port.Write(_hexval, 0, 1);
System.Threading.Thread.Sleep(1);
}
}
catch (Exception ex)
{
Debug.WriteLine("Error: Failed to SEND" + data + "\n" + ex.Message + "\n");
}
}
else
{
Debug.WriteLine("Error: Port is not open. Please open the connection and try again.");
}
}
}
}
注意:您可能需要安装 USB 驱动程序。 AND Driver Software.
创建一个可以与 RichTextBox 一起使用的扩展方法。
创建类(ControlExtensions)
见How to update a RichTextBox from BackgroundWorker using BeginInvoke
- 在 VS 菜单上,选择 项目
- 选择添加类(名称:ControlExtensions.cs)
ControlExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ReadSerialPort
{
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
if (control.InvokeRequired) control.Invoke(new MethodInvoker(action), null);
else action.Invoke();
}
}
}
接下来我们将向 Form1 添加一些控件和代码。
打开属性窗口
打开解决方案资源管理器
- 在VS菜单中,选择查看
- 选择解决方案资源管理器
- 在解决方案资源管理器中,双击 Form1.cs 以打开设计器。
将“连接”按钮添加到 Form1
- 在VS菜单中,选择查看
- 选择工具箱
- 选择按钮
- 点击 Form1 将按钮添加到表单中
- 在属性窗口中,为“button1”设置(名称):btnConnect;设置文本:连接
- 在属性窗口中,单击(事件)。双击单击将事件处理程序添加到 Form1.cs
将“断开连接”按钮添加到 Form1
- 在VS菜单中,选择查看
- 选择工具箱
- 选择按钮
- 点击 Form1 将按钮添加到表单中
- 在属性窗口中,对于“button1”,设置 (name):btnDisconnect;设置文字:断开连接
- 在“属性”窗口中,单击(事件)。双击单击将事件处理程序添加到 Form1.cs
将 RichTextBox 添加到 Form1
- 在VS菜单中,选择查看
- 选择工具箱
- 选择富文本框
- 点击 Form1 将按钮添加到表单中
- 在“属性”窗口中,为“richTextBox1”设置(名称):richTextBoxReceivedData
将“Load”事件处理程序添加到 Form1
- 在“属性”窗口中,对于“Form1”,单击(事件)。双击加载以将事件处理程序添加到 Form1.cs
将“FormClosing”事件处理程序添加到 Form1
- 在“属性”窗口中,对于“Form1”,单击 (事件)。双击 FormClosing 将事件处理程序添加到 Form1.cs
修改 Form1.cs 代码
- 在解决方案资源管理器中,右键单击 Form1.cs
- 选择查看代码
选项 1(不会自动检测 USB 设备何时插入/拔出):
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
namespace ReadSerialPort
{
public partial class Form1 : Form
{
private HelperSerialPort helperSerialPort = new HelperSerialPort();
private ComPorts comPorts = null;
public Form1()
{
InitializeComponent();
}
private void FrmMain_Load(object sender, EventArgs e)
{
//get COM port info
GetComPorts();
}
private void HelperSerialPort_DataReceived(object sender, SerialPortDataReceivedEventArgs e)
{
Debug.WriteLine("Data: " + e.Data);
richTextBoxReceivedData.Invoke(() =>
{
richTextBoxReceivedData.AppendText(e.Data);
richTextBoxReceivedData.Refresh();
});
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (helperSerialPort.Port == null || !helperSerialPort.Port.IsOpen)
{
helperSerialPort.Connect("COM3", PortBaudRate.Baud9600);
helperSerialPort.DataReceived += HelperSerialPort_DataReceived;
}
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
helperSerialPort.Dispose();
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (helperSerialPort != null && helperSerialPort.Port != null)
{
helperSerialPort.Dispose();
helperSerialPort = null;
}
}
private void GetComPorts()
{
//get COM port info
comPorts = helperSerialPort.GetComPortInfo();
foreach (ComPortInfo cpInfo in comPorts.Ports)
{
Debug.WriteLine("Name: '" + cpInfo.Name + "' PortName: '" + cpInfo.PortName + "'");
}
}
}
}
选项 2(自动检测 USB 设备何时插入/拔出):
注意:以下部分代码来自:Check for device change (add/remove) events
创建类(UsbDeviceNotification)
- 在 VS 菜单上,选择 项目
- 选择添加类(名称:UsbDeviceNotification.cs)
UsbDeviceNotification.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace ReadSerialPort
{
public static class UsbDeviceNotification
{
public const int DbtDevicearrival = 0x8000; // system detected a new device
public const int DbtDeviceremovecomplete = 0x8004; // device is gone
public const int WmDevicechange = 0x0219; // device change event
private const int DbtDevtypDeviceinterface = 5;
private static readonly Guid GuidDevinterfaceUSBDevice = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); // USB devices
private static IntPtr notificationHandle;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);
[DllImport("user32.dll")]
private static extern bool UnregisterDeviceNotification(IntPtr handle);
[StructLayout(LayoutKind.Sequential)]
private struct DevBroadcastDeviceinterface
{
internal int Size;
internal int DeviceType;
internal int Reserved;
internal Guid ClassGuid;
internal short Name;
}
/// <summary>
/// Registers a window to receive notifications when USB devices are plugged or unplugged.
/// </summary>
/// <param name="windowHandle">Handle to the window receiving notifications.</param>
public static void RegisterUsbDeviceNotification(IntPtr windowHandle)
{
DevBroadcastDeviceinterface dbi = new DevBroadcastDeviceinterface
{
DeviceType = DbtDevtypDeviceinterface,
Reserved = 0,
ClassGuid = GuidDevinterfaceUSBDevice,
Name = 0
};
dbi.Size = Marshal.SizeOf(dbi);
IntPtr buffer = Marshal.AllocHGlobal(dbi.Size);
Marshal.StructureToPtr(dbi, buffer, true);
notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);
}
/// <summary>
/// Unregisters the window for USB device notifications
/// </summary>
public static void UnregisterUsbDeviceNotification()
{
UnregisterDeviceNotification(notificationHandle);
}
}
}
然后在Form1.cs中使用如下代码:
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
namespace ReadSerialPort
{
public partial class Form1 : Form
{
private HelperSerialPort helperSerialPort = new HelperSerialPort();
private ComPorts comPorts = null;
public Form1()
{
InitializeComponent();
}
private void FrmMain_Load(object sender, EventArgs e)
{
//get COM port info
GetComPorts();
}
private void HelperSerialPort_DataReceived(object sender, SerialPortDataReceivedEventArgs e)
{
Debug.WriteLine("Data: " + e.Data);
richTextBoxReceivedData.Invoke(() =>
{
richTextBoxReceivedData.AppendText(e.Data);
richTextBoxReceivedData.Refresh();
});
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (helperSerialPort.Port == null || !helperSerialPort.Port.IsOpen)
{
helperSerialPort.Connect("COM3", PortBaudRate.Baud9600);
helperSerialPort.DataReceived += HelperSerialPort_DataReceived;
}
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
helperSerialPort.Dispose();
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (helperSerialPort != null && helperSerialPort.Port != null)
{
helperSerialPort.Dispose();
helperSerialPort = null;
}
}
private void GetComPorts()
{
//use SynchronizationContext.Current with ThreadPool to avoid the following error:
//Transition into COM context...for this RuntimeCallableWrapper failed with the following error:
//An outgoing call cannot be made since the application is dispatching an input-synchronous call.
//Exception from HRESULT: 0x8001010D (RPC_E_CANTCALLOUT_INPUTSYNCCALL)
var sc = System.Threading.SynchronizationContext.Current;
System.Threading.ThreadPool.QueueUserWorkItem(delegate
{
//do work on threadpool
sc.Post(delegate
{
//get COM port info
comPorts = helperSerialPort.GetComPortInfo();
foreach (ComPortInfo cpInfo in comPorts.Ports)
{
Debug.WriteLine("Name: '" + cpInfo.Name + "' PortName: '" + cpInfo.PortName + "'");
}
}, null);
});
}
private void UsbDeviceAdded()
{
//ToDo: add desired code
Debug.WriteLine("Info: USB device added");
//get COM port info
GetComPorts();
}
private void UsbDeviceRemoved()
{
//ToDo: add desired code
Debug.WriteLine("Info: USB device removed");
//get COM port info
GetComPorts();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == UsbDeviceNotification.WmDevicechange)
{
switch ((int)m.WParam)
{
case UsbDeviceNotification.DbtDeviceremovecomplete:
UsbDeviceRemoved();
break;
case UsbDeviceNotification.DbtDevicearrival:
UsbDeviceAdded();
break;
}
}
}
}
}