2
votes

I have some problems using System.IO.Ports in C# to read data from a serial port. The only method thar works is the "Read" method, but it only read "defined" number of characters and i need to read all available data like "Tera Term" or "Hercules" do. Here is my DLL method that reads the buffer:

public String ReadMessage(String port, int timeout, int dataSize)
{
    String res;

    try
    {
        _serial_port = new SerialPort();
        _serial_port.PortName = port;
        _serial_port.BaudRate = 19200;
        _serial_port.Parity = Parity.None;
        _serial_port.DataBits = 8;
        _serial_port.StopBits = StopBits.One;
        _serial_port.Handshake = Handshake.None;
        _serial_port.ReadTimeout = timeout;
        _serial_port.DtrEnable = true;
        _serial_port.RtsEnable = true;

        _serial_port.Open();
        _serial_port.DiscardInBuffer();

        int totalBytes = _serial_port.BytesToRead;

        if (totalBytes > 0)
        {
            byte[] buffer = new byte[totalBytes];
            _serial_port.Read(buffer, 0, totalBytes);
            res = ByteArrayToString(buffer);                   
        }
        else
        {
            byte[] buffer = new byte[dataSize];

            for (int len = 0; len < buffer.Length;)
            {
                len += _serial_port.Read(buffer, len, buffer.Length - len);
            }

            res = ByteArrayToString(buffer);                    
        }

        _serial_port.Close();

        return res;
    }
    catch (Exception ex)
    {
        if (ex is UnauthorizedAccessException || ex is TimeoutException)
        {
            _serial_port.Close();
        }

        return ex.ToString();
    }
}

I know there are other methods to read data like: "ReadByte", "ReadChar", "ReadExisting", "ReadLine" and "ReadTo" but none of them are working, am i doing something wrong?

1
Well, you open port, discard anything is in input buffer and then expect it to contain valid data? Why do you get "all the data in buffer" when probably there is any and get whole buffer when there is no data?Dominik Szymański
Thanks, i use the _serial_port.DiscardInBuffer() method to clean the previous "read" data in buffer.Noe Cano
So what do you consider end of transmission? Do you send some end character / character sequence? Maybe some period of "silence"? Maybe you should consider attaching length in the beginning of the transmission?Dominik Szymański
Reading the buffer automatically clears it. Discarding it again may loose data that is received in the mean time.imqqmi
Thanks for your answer, the problem here is that for example in programs like Tera Term or Hercules it is not necessary to specify when the string is finished transmitting or the size of the string, I think it sends a character at the end of each transmission to warn To the receiver that the total of the message was sent. I believe that the sender sends some character / value to indicate that the message has been transmitted, but I do not know what it is.Noe Cano

1 Answers

3
votes

The serial port is a stream device, it can continuously receive data. You'll need to set a timer as long the serial port is open to continuously check if there's anything in the buffer and copy it to a buffer or file on disk as soon as possible to prevent a buffer overflow.

You need to check the data contents to find out what is received and when all of it is received. This depends on the sender. You can't just say receive all there is to receive, it's a continous stream as far as the serial port is concerned. The actual data needs to specify the protocol, start and stop conditions.

EDIT

Here's a code snippet I use to capture floppy disk data through a usb to rs232 device at 5mbit/sec:

    // Capture handler
    private void timer2_Tick(object sender, EventArgs e)
    {
        // Read data from serial port, poll every 10ms
        // If data is there, read a block and write it to array
        int bytestoread = 0;
        //byte buf;

        timer2.Stop();
        try
        {
            bytestoread = serialPort1.BytesToRead;
        }

        catch (InvalidOperationException ex)
        {
            tbr.Append("Serial connection lost. Exception type:" + ex.ToString());
            if ((uint)ex.HResult == 0x80131509)
            {
                timer2.Stop();
            }
        }

        if (serialPort1.IsOpen)
        {
            if (bytestoread != 0)
            {
                bytespersecond += bytestoread;

                byte[] temp = new byte[bytestoread];

                if (serialPort1.IsOpen)
                    serialPort1.Read(temp, 0, bytestoread);

                tempbuffer.Add(temp);
                processing.indexrxbuf += bytestoread;
                recentreadbuflength += bytestoread;

                //update the scatterplot, this may have a performance hit
                processing.rxbuf = tempbuffer.SelectMany(a => a).ToArray();
                rxbuf = processing.rxbuf;
                if (Setrxbufcontrol == null) return;
                Setrxbufcontrol();

            }
            timer2.Start();
        }
    }

setup code for serial port. I defined large buffers to prevent overflows:

        serialPort1.BaudRate = 5000000;
        serialPort1.NewLine = "\r\n";
        serialPort1.ReceivedBytesThreshold = 500000;
        serialPort1.ReadBufferSize = 1048576;

EDIT 2

Steps to read from a serial port:

  1. Define a received data array to hold the data
  2. Open serial port
  3. Set a timer to read data continuously until user hits disconnect button
  4. timer tick method checks the length of data in serial buffer, then read that length into the receive buffer defined in step 1. Keep track of total received data by adding the serial buffer length to a static variable.
  5. In another timer you could check the received data array for new lines. Copy all data to an array of strings, or to a textbox. Or just copy the data as a string to the textbox to see what's in it.