Search code examples
c#serial-portusbusbserial

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


Im traying to write a program to read data from a old AND scale using a serial to USB converter cable to display in a textbox .i was able to write a program successfully to read data only from a RS232 serial cable, but when I attached a serial to USB cable to it, it only displayed some numbers and others are just question marks. (Ex: ???0.3?2?)

method i used to read data .

 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)    
            {
            }
        }
    }

Is that a problem that I can over come through a code or is that the serial to USB cable is malfunctioning ?


Solution

  • I tested the code below with a USB serial port device, which may also work with your scale. Some of the port settings were found by downloading/installing WinCT (RsCom, RsKey & RsWeight)). Then, in the Windows Start menu under A&D WinCT, select either RsCom or RsKey. Using RsCom or RsKey is an easy way to check that your USB cable/connection is working. I used both "RsKey" and "RsCom" with my USB serial device, and it seemed to work.

    Create a WinForms project

    VS 2017:

    • Open Visual Studio
    • Expand Installed
    • Expand Visual C#
    • Click Windows Desktop
    • Select Windows Forms App (.NET Framework)
    • Specify project name (name: ReadSerialPort)
    • Click OK

    VS 2019:

    • Open Visual Studio
    • Click Continue without code
    • Click File
    • Select New
    • Select Project
    • C# Windows Desktop
    • Click Windows Forms App (.NET Framework)
    • Click Next
    • Specify project name (name: ReadSerialPort)
    • Click Create

    Note: From this point forward, the process is the same for both VS 2017 and VS 2019.

    Add class: SerialPortDataReceivedEventArgs

    Note: This class will be used with an event that sends the data received from the serial port device to a subscriber.

    • On VS menu, select Project
    • Select Add Class (name: 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;
            }
        }
    }
    

    Add Reference to System.Management

    • In VS menu, select Project
    • Select Add Reference
    • Expand Assemblies
    • Check System.Management
    • Click OK

    Add class: ComPorts

    • On VS menu, select Project
    • Select Add Class (name: 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;
            }
        }
    }
    

    Add class: HelperSerialPort

    • On VS menu, select Project
    • Select Add Class (name: 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.");
                }
            }
        }
    }
    

    Note: You may need to install a USB driver. AND Driver Software.

    Create an extension method that can be used with RichTextBox.

    Create class (ControlExtensions)

    See How to update a RichTextBox from BackgroundWorker using BeginInvoke

    • On VS menu, select Project
    • Select Add Class (name: 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();
            }
        }
    }
    

    Next we'll add some controls and code to Form1.

    Open Properties Window

    • In VS menu, select View
    • Select Properties Window

    Open Solution Explorer

    • In VS menu, select View
    • Select Solution Explorer
    • In Solution Explorer, double-click Form1.cs to open the designer.

    Add "Connect" button to Form1

    • In VS menu, select View
    • Select Toolbox
    • Select Button
    • Click on Form1 to add the button to the form
    • In Properties Window, for "button1", set (name): btnConnect; set Text: Connect
    • In Properties Window, click (Events). Double-click Click to add event handler to Form1.cs

    Add "Disconnect" button to Form1

    • In VS menu, select View
    • Select Toolbox
    • Select Button
    • Click on Form1 to add the button to the form
    • In Properties Window, for "button1", set (name): btnDisconnect; set Text: Disconnect
    • In Properties Window, click (Events). Double-click Click to add event handler to Form1.cs

    Add RichTextBox to Form1

    • In VS menu, select View
    • Select Toolbox
    • Select RichTextBox
    • Click on Form1 to add the button to the form
    • In Properties Window, for "richTextBox1", set (name): richTextBoxReceivedData

    Add "Load" event handler to Form1

    • In Properties Window, for "Form1"", click (Events). Double-click Load to add event handler to Form1.cs

    Add "FormClosing" event handler to Form1

    • In Properties Window, for "Form1"", click (Events). Double-click FormClosing to add event handler to Form1.cs

    Modify Form1.cs code

    • In Solution Explorer, right-click Form1.cs
    • Select View Code

    Option 1 (doesn't automatically detect when a USB device is plugged 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 + "'");
                }
            }
        }
    }
    

    Option 2 (automatically detect when a USB device is plugged in/unplugged):

    Note: Some of the code below is from: Check for device change (add/remove) events

    Create class (UsbDeviceNotification)

    • On VS menu, select Project
    • Select Add Class (name: 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);
            }
    
        }
    }
    

    Then use the following code in 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;
                    }
                }
            }
        }
    }
    

    Update:

    Here's some additional info that may be useful:

    Open PowerShell and run the following:

    • Get-CimInstance -Namespace Root\Cimv2 -Query "Select * From Win32_SerialPort Where Name like '%COM%'"
    • Get-CimInstance -Namespace Root\Cimv2 -Query "Select * From Win32_SerialPortConfiguration"
    • Get-CimInstance -Namespace Root\Cimv2 -Query "Select * From Win32_PnPEntity where PnPClass = 'Ports' and Name like '%COM%'"
    • mode