简体   繁体   中英

How do I troubleshoot SerialPort.Read() always return 0 in C#?

I have communicating to Display monitor using RS-232C 9-Pin D-Sub to Stereo Cable to control power ON/OFF, Volume Up/Down etc. I created a sample program based on Microsoft Docs System.IO.Ports. I can open COM PORT, write some byte arrays (based on protocol document) and no issue. But, reading in another thread always time out and receives '0' only. According to document, I should receive ACK or NAK (which will send back to me as byte array starting with 0xAA as well). I call Write() method in driver main class.

How can I troubleshoot for this issue? My reading byte array method is wrong? Thank you all for the input.

public class ComPortDemo
{
    private static bool _continue;
    private static SerialPort _serialPort;

    public ComPortDemo()
    {
        _serialPort = new SerialPort
        {
            PortName = "COM1",
            Parity = Parity.None,
            StopBits = StopBits.One,
            BaudRate = 9600,
            DataBits = 8,
            WriteTimeout = 2000,
            ReadTimeout = 5000,
            Handshake = Handshake.None,
        };

        _serialPort.DataReceived += SerialPortOnDataReceived;
        _serialPort.ErrorReceived += SerialPortOnErrorReceived;
        _serialPort.PinChanged += SerialPortOnPinChanged;
        _serialPort.DtrEnable = true;
        _serialPort.RtsEnable = true;
    }

    private void SerialPortOnPinChanged(object sender, SerialPinChangedEventArgs e)
    {
        Console.WriteLine($"Pin changed event occurred. " + e.EventType);
    }

    public void Write()
    {
        int bufferLength = 6;
        byte[] commands = new byte[bufferLength];

        commands[0] = 0xAA;
        commands[1] = 0x11;
        commands[2] = 0x02;
        commands[3] = 0x01;
        commands[4] = 0x01;
        commands[5] = 0x15;

        if (_serialPort.IsOpen)
            _serialPort.Close();

        _serialPort.Open();
        _continue = true;
        Thread readThread = new Thread(Read);
        readThread.Start();
        Console.WriteLine("Reader thread started and wait for response..");

        Console.Write("Sent: ");
        foreach (var command in commands)
        {
            Console.Write($"0x{command:X2} ");
        }

        Console.WriteLine();

        _serialPort.DiscardInBuffer();
        _serialPort.DiscardOutBuffer();
        _serialPort.Write(commands, 0, commands.Length);

        Console.WriteLine("ENTER to end reading thread..");
        Console.ReadLine();
        _continue = false;
        readThread.Join();
    }

    private static void SerialPortOnErrorReceived(object sender, SerialErrorReceivedEventArgs e)
    {

        Console.WriteLine("serial port on error : " + e.EventType);
    }

    private static void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        //var serialPort = (SerialPort) sender;
        //string data = serialPort.ReadExisting();
        //Console.WriteLine($"{data}");
        Console.WriteLine("data received event");
    }

    public static void Read()
    {

        while (_continue)
        {
            try
            {
                if (_serialPort.BytesToRead <= 0)
                {
                    Console.WriteLine("No data to read");
                }

                byte[] buffer = new byte[7];
                Console.WriteLine("trying to read..");
                int readByte = _serialPort.Read(buffer, 0, buffer.Length);
                Console.WriteLine($"Read byte : {readByte}");
                //if (readByte > 0)
                foreach (var b in buffer)
                    Console.Write($"0x{b:X2} ");

            }
            catch (TimeoutException toe)
            {
                Console.WriteLine(toe.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
            }

            Thread.Sleep(2000);
        }
    }

    public void Close()
    {
        _continue = false;
        if(_serialPort.IsOpen)
            _serialPort.Close();
    }
}

If You can, use my simple wrapper of COM port - it uses base streams and TPL library, therefore should eliminate some, maybe even most problems with threading work needed.

You will have to create a custom ICommunicationDeviceSettings implementation that will hold typical serial port settings (take a look at SetupDeviceOptions method, You will know what exactly is needed).

i hope that it will help You. Bare in mind that this implementation was made for long message transfer, but still - should work for You.

using System;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SerialPortWrapper
{
    /// <summary>
    /// Wraps SerialPort for future use with interfaces
    /// </summary>
    public class SerialPortAdapter : ICommunicationDeviceAsync
    {
        private readonly SerialPort _port = new SerialPort();
        private Encoding _encoding;
        private const int ByteBufferSize = 4092;    // Optimal size of max single data portion received buffer
        private byte[] _buffer = new byte[ByteBufferSize];

        private readonly object _lock = new object();
        private bool _disposed;

        public event EventHandler<string> DataReceived;

        public SerialPortAdapter(ICommunicationDeviceSettings settings) => Setup(settings);

        public bool IsOpen => _port?.IsOpen == true;
        public int ConfirmationTimeout { get; private set; }
        public int CompletitionTimeout { get; private set; }
        public string Name { get; private set; }

        /// <summary>
        /// Sets up all of the necessary parameters for this instance of the device
        /// </summary>
        /// <param name="settings">Configuration from AppSettings for this type of device</param>
        /// <returns>True if successful</returns>
        public void Setup(ICommunicationDeviceSettings settings)
        {
            if (!(settings is SerialPortSettings set))
                throw new ArgumentNullException(nameof(settings));

            SetUpDeviceOptions(set);
        }

        private bool PortNumberExists(int portNumber) =>
            SerialPort.GetPortNames().Contains($"COM{portNumber}");

        private void SetUpDeviceOptions(SerialPortSettings settings)
        {
            _port.PortName = $"COM{settings.PortNumber}";
            _port.BaudRate = settings.BaudRate;
            _port.DataBits = settings.DataBits;
            _port.Parity = settings.Parity;
            _port.StopBits = settings.StopBits;
            _port.ReadTimeout = settings.ReadTimeout;
            _port.WriteTimeout = settings.WriteTimeout;
            _encoding = _port.Encoding = Encoding.GetEncoding(settings.Encoding);
            _port.DtrEnable = false;
            _port.RtsEnable = false;
            _port.DiscardNull = true;
            CompletitionTimeout = settings.CompletitionTimeout;
            ConfirmationTimeout = settings.ConfirmationTimeout;
            Name = settings.Name;
        }

        public void Open()
        {
            lock (_lock)
            {
                if (!_port.IsOpen)
                    _port.Open();
            }
        }

        public void Close()
        {
            DiscardInBuffer();
            _port.Close();
        }

        public async Task CloseAsync()
        {
            await Task.Run(() =>
            {
                DiscardInBuffer();
                _port.Close();
            })
            .ConfigureAwait(false);
        }

        public string Receive()
        {
            if (!_port.IsOpen)
                return string.Empty;

            try
            {
                int bytesRead = _port.Read(_buffer, 0, _buffer.Length);
                string rawData = _encoding.GetString(_buffer, 0, bytesRead);
                OnDataReceived(rawData);
                return rawData;
            }
            catch (IOException)
            {
                return string.Empty; // Receiving failed - port closed or disconnected
            }
        }

        /// <summary>
        /// Constantly monitors incoming communication data, filters it and generates Commands (ICommandModel)
        /// </summary>
        public async Task<string> ReceiveAsync(CancellationToken ct)
        {
            if (!_port.IsOpen)
                return string.Empty;

            try
            {
                ct.ThrowIfCancellationRequested();
                int bytesRead = await _port.BaseStream.ReadAsync(_buffer, 0, _buffer.Length, ct).ConfigureAwait(false);
                string rawData = _encoding.GetString(_buffer, 0, bytesRead);
                OnDataReceived(rawData);
                return rawData;
            }
            catch (Exception ex) when (ex is IOException || ex is OperationCanceledException)
            {
                // Only known way to handle sudden port failure AND cancellation - known SerialPort implementation hiccup.
                throw new OperationCanceledException("System IO exception in BaseStream.ReadAsync - handled, expected, re-thrown. Either task was canceled or port has been closed", ex, ct);
            }
        }

        /// <summary>
        /// Sends a raw data string.
        /// </summary>
        /// <param name="data">Single data string to transfer</param>
        /// <returns>True if sending was successful</returns>
        public bool Send(string data)
        {
            if (!CanSend(data))
                return false;

            byte[] buffer = _encoding.GetBytes(data);
            try
            {
                _port.Write(buffer, 0, buffer.Length);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Sends a raw data string asynchronously.
        /// </summary>
        /// <param name="data">Single data string to transfer</param>
        /// <returns>True if sending was successful</returns>
        public async Task<bool> SendAsync(string data, CancellationToken ct)
        {
            if (!CanSend(data))
                return false;

            byte[] buffer = _encoding.GetBytes(data);
            try
            {
                ct.ThrowIfCancellationRequested();
                await _port.BaseStream.WriteAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false);
                return true;
            }
            catch
            {
                return false;
            }
        }

        private bool CanSend(string data) => !IsOpen || string.IsNullOrEmpty(data);

        /// <summary>
        /// Clears data in 'received' buffer
        /// </summary>
        public void DiscardInBuffer()
        {
            _port.BaseStream.Flush();
            _port.DiscardInBuffer();
            _buffer = new byte[ByteBufferSize];
        }

        protected virtual void OnDataReceived(string rawData) => DataReceived?.Invoke(this, rawData);

        #region IDisposable implementation

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                if (_port?.IsOpen ?? false)
                    _port.Close();
            }
            _disposed = true;
        }

        #endregion IDisposable implementation
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM