从秤到 PC 的串行到 USB 电缆,一些值只是问号

Serial to USB cable from a scale to PC ,some values are just question marks

我正在尝试编写一个程序来使用串行到 USB 转换器电缆从旧的 AND 秤读取数据以显示在文本框中。我能够成功编写一个程序来仅从 RS232 串行电缆读取数据,但是当我将串口连接到 USB 电缆时,它只显示一些数字,其他的只是问号。 (例如:???0.3?2?)

我用来读取数据的方法。

 private void PortOnDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        while (_port.BytesToRead > 0)
        {
            // PostKeys
            var original = _port.ReadExisting();
         
            // Reformat string to fit SendKeys()
            var reformattedString = DefaultFormatter.Reformat(original);
            try
            {
                SendKeys.SendWait(reformattedString);
            }
            // Handle exception caused if keys are sent to an application
            // not handling keys
            catch(Exception ex)    
            {
            }
        }
    }

这是我可以通过代码解决的问题还是串口转 USB 电缆出现故障?

我用 USB 串口设备测试了下面的代码,它可能也适用于您的体重秤。 downloading/installing WinCT (RsCom, RsKey & RsWeight)) 发现了一些端口设置。然后,在 A&D WinCT 下的 Windows 开始菜单中,select RsComRsKey。使用 RsComRsKey 是检查 USB cable/connection 是否正常工作的简单方法。我在我的 USB 串行设备上同时使用了“RsKey”和“RsCom”,它似乎工作正常。

创建 WinForms 项目

VS 2017:

  • 打开Visual Studio
  • 展开已安装
  • 展开Visual C#
  • 单击Windows桌面
  • Select Windows 表单应用程序 (.NET Framework)
  • 指定项目名称(名称:ReadSerialPort)
  • 单击确定

VS 2019:

  • 打开Visual Studio
  • 单击继续,无需代码
  • 单击文件
  • Select 新建
  • Select 项目
  • C# Windows 桌面
  • 单击 Windows 表单应用程序 (.NET Framework)
  • 点击下一步
  • 指定项目名称(名称:ReadSerialPort)
  • 单击创建

注意:从现在开始,VS 2017 和 VS 2019 的过程相同。

添加class:SerialPortDataReceivedEventArgs

注意:此class将与将从串行端口设备接收到的数据发送到订阅者的事件一起使用。

  • 在 VS 菜单上,select 项目
  • Select添加Class(名称: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 菜单中,select 项目
  • Select 添加引用
  • 展开程序集
  • 勾选System.Management
  • 单击确定

添加class: ComPorts

  • 在 VS 菜单上,select 项目
  • Select添加Class(名称: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;
        }
    }
}

添加class:HelperSerialPort

  • 在 VS 菜单上,select 项目
  • Select添加Class(名称: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 一起使用的扩展方法。

创建 class (ControlExtensions)

How to update a RichTextBox from BackgroundWorker using BeginInvoke

  • 在 VS 菜单上,select 项目
  • Select添加Class(名称: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 添加一些控件和代码。

打开属性 Window

  • 在VS菜单中,select 查看
  • Select属性Window

打开解决方案资源管理器

  • 在VS菜单中,select 查看
  • Select 解决方案资源管理器
  • 在解决方案资源管理器中,双击 Form1.cs 打开设计器。

将“连接”按钮添加到 Form1

  • 在VS菜单中,select 查看
  • Select 工具箱
  • Select 按钮
  • 单击 Form1 将按钮添加到表单
  • 在属性 Window 中,为“button1”设置 (name): btnConnect;设置 文本:连接
  • 在属性 Window 中,单击 (事件)。双击单击将事件处理程序添加到Form1.cs

向 Form1 添加“断开连接”按钮

  • 在VS菜单中,select 查看
  • Select 工具箱
  • Select 按钮
  • 单击 Form1 将按钮添加到表单
  • 在属性 Window 中,为“button1”设置 (name): btnDisconnect;设置 文本:断开连接
  • 在属性 Window 中,单击 (事件)。双击单击将事件处理程序添加到Form1.cs

将 RichTextBox 添加到 Form1

  • 在VS菜单中,select 查看
  • Select 工具箱
  • Select RichTextBox
  • 单击 Form1 将按钮添加到表单
  • 在属性 Window 中,为“richTextBox1”设置 (name): richTextBoxReceivedData

将“加载”事件处理程序添加到 Form1

  • 在属性 Window 中,对于“Form1”,单击 (事件)。双击 Load 以将事件处理程序添加到 Form1.cs

将“FormClosing”事件处理程序添加到 Form1

  • 在属性 Window 中,对于“Form1”,单击 (事件)。双击 FormClosing 以将事件处理程序添加到 Form1.cs

修改Form1.cs代码

  • 在解决方案资源管理器中,右键单击 Form1.cs
  • Select 查看代码

选项 1(插入 USB 设备时不自动检测 in/unplugged):

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设备时自动检测in/unplugged):

:以下部分代码来自:Check for device change (add/remove) events

创建class (UsbDeviceNotification)

  • 在 VS 菜单上,select 项目
  • Select添加Class(名称: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;
                }
            }
        }
    }
}