PublicAffair
PublicAffair

Reputation: 55

SerialPort in c# , Data Receive Incomplete Message

I'm working on Serialport. I'm facing a new problem that once I receive data my data are incomplete. How can I check if my data are complete then process them, and if not, don't process them?

Here are my data receive and my send function:

 private void Send(byte[] cmd)
        {
            bResponse = new byte[0];
            Write(cmd);        
        }

 void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int iCount = comPort.BytesToRead;
            byte[] bBuffer = new byte[iCount];
            comPort.Read(bBuffer, 0, iCount)
            if (bBuffer.Length == 1 && bBuffer[0] == ACK)
                Write(new byte[] { ENQ });
            else if (bBuffer.Length == 1 && bBuffer[0] == NAK)
            {
                Debug.WriteLine("Incomplete Message detected!");  
            }
            else
            {
                bResponse = bResponse.Concat(bBuffer).ToArray();
                    rResponse = Decode(bResponse);
                    Write(new byte[] { ACK });
            }
        }

I know my data are received in a few packages and I need to wait until the response is complete, but I don't know based on the code above. How should I check whether the data are complete to determine whether to wait? (P.S: The size of the received response varies.)

Upvotes: 1

Views: 3879

Answers (3)

DasKrümelmonster
DasKrümelmonster

Reputation: 6060

I have some code for you. First, you implement the DataReceived Event (as you have done already). This event is only called when there is data to process. While I would not call it interrupt-based (as in "realtime capable") is is definitely not polling. Which is good.

Second: When the event is called you may have only one byte, but there may be more bytes. To capture each packet you need to implement an custom buffer.

Third: After you append one byte to your buffer, you check whether the buffer contains a valid packet. If so, process it. If not: Append another one. If no bytes are left, wait for the event to be called again.

In code it looks like this:

const BUFFERLENGTH = 17; // Bytes
byte[] buffer = new byte[BUFFERLENGTH];


private void COM_Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var port = (SerialPort)sender;
    while (port.BytesToRead > 0)
    {
        var data = (byte)port.ReadByte();
        Read(data);
    }
}

private void Read(byte value)
{
    // Append Byte to buffer
    System.Buffer.BlockCopy(buffer, 1, buffer, 0, BUFFERLENGTH- 1);
    buffer[BUFFERLENGTH - 1] = value;

    // Check for valid Packet
    if (IsValidPacket(buffer))
    {
        // Yeah! Gotcha :-)
        // Now copy your Packet from the Buffer to your struct/whatever
    }
}

private bool IsValidPacket(byte[] buffer)
{
    // Todo: Check whether buffer contains a valid Packet.
    // Some think like:
    // return buffer != null && buffer[0] == 0xF4 && buffer[2] == buffer.length
    throw new NotImplementedException();
}

Note that I did not "append the byte to the buffer". I discarded the first byte, shifted every byte by one position and inserted the new byte at the end. If a valid Packet was found I could just copy it in one block into a struct. So the buffer size is always constant and exactly as long as one packet.

This may not be the fastest code out there (because it's reading each byte separately) but it works well for me :-)

Oh, and remember to use Begininvoke() if you want to display that stuff in your GUI.

Upvotes: 0

Random IT Guy
Random IT Guy

Reputation: 623

Example of old project, notice the firstindex, lastindex, you put in a character to know the length, the start/end character is predefined and can be any character you choose, just be sure not to take any common characters

This is for tcp/ip, but same principle can be used for serialport

    public void ReceiveMessage(IAsyncResult ar) 
    {
        int bytesRead;
        try
        {
            lock (client1.GetStream()) 
            {
                bytesRead = client1.GetStream().EndRead(ar);
            }
            string messageReceived = System.Text.Encoding.ASCII.GetString(data, 0, bytesRead);
            received = messageReceived;
            int firstindex = received.IndexOf((char)2);
            int lastindex = received.IndexOf((char)3);
            if (firstindex > 0 && lastindex > 0)
            {
                string first = received.Substring(firstindex, 1);
                string last = received.Substring(lastindex, 1);
            }
            lock (client1.GetStream())
            {
                client1.GetStream().BeginRead(data, 0, System.Convert.ToInt32(client1.ReceiveBufferSize), ReceiveMessage, null);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

Upvotes: 0

There is no built-in concept of completeness or packet size.

You'll have to append to a buffer until you see some recognizable end-of-packet pattern that you (or someone else) defined as part of the protocol specification. - And then probably time out after a while if you haven't seen what you are looking for.

Upvotes: 2

Related Questions