Search code examples
c#serial-portportrfid

RFID Doesn't answer always properly


I have an RFID card reader connected to my pc on serial port. It's using RS485, so I need switching between send and receive state. The communication frames contains header and CRC (CRC16 ccitt - Xmodem). After every writing on the port I'm waiting the answer, then computing the CRC and if it failed, request frame again. Then if everything correct process it.

It works fine with the "simple" commands. (Request Firmware version, Enable/Disable antenna, etc..).

With the important commands (Logging into the reader's interface, config. it, etc..) I'm facing the next: Rarely the answer comes correctly, with a maximum delay of 5 secs, but in the most of the cases, I don't get anything on the buffer. I can wait for minutes, but nothing.

Conclusion: If I get answer it happens in the first seconds, if I don't I can wait anytime, it won't happen.

My question is: Could it be the hardware's fault, or maybe I miss something in my software?

Here is the send & receive part of my code:

int size;
bool msg_ok = false;
do
{
    int max_attemps = 50;

    port.DtrEnable = true;
    port.RtsEnable = false;

    port.Write(fullMsg, 0, fullMsg.Length);

    port.DtrEnable = false;
    port.RtsEnable = true;

    do
    {
        Thread.Sleep(200);
        size = port.BytesToRead;
    }while(size <= 3 && max_attemps-- > 0);

    if(size > 3){
        answer = new byte[size];
        port.Read(answer, 0, size);

        int end = answer.Length - 1; //Trim 0-s after end
        while (answer[end] == 0)
            --end;

        int start = 0;
        while (answer[start] == 0) //Trim 0-s before header
            ++start;

        trimmed = new byte[(end - start) + 1];
        Array.Copy(answer, start, trimmed, 0, (end - start) + 1);

        checkSum = new byte[2];
        checkSum = crc.ComputeChecksumBytes(trimmed, trimmed.Length); //Calculate crc

        if (checkSum[0] == trimmed[trimmed.Length - 1] && checkSum[1] == trimmed[trimmed.Length - 2])
        {
                msg_ok = true; //If it's still false on the end, restart this whole block and request again, if it's true, I can send the answer for processing
        }
    } else {
        Console.WriteLine("Timed out.");
    }
}while(!msg_ok);

Solution

  • When data is sent over a serial port, the operating system buffers the data as it arrives. If you query the data when only some of it has arrived, you will get a partial packet. You need to keep reading until you receive the full packet before you start trying to decode it. Otherwise your decode will fail on the first half of the packet, fail on the second half, and then sit waiting for another message that will never come.

    The best approach for using a serial port is to subscribe to the DataReceived event, because this means you are called by the port if and when data arrives. This avoids having to sleep to try to get around the timing issues. You will still sometimes need to stitch several chunks of received data together to form a valid packet however, so you should write your code to keep reading and appending into a receive buffer until it recognises a valid, complete packet.

    You also shouldn't need to flip the handshaking bits unless the device on the other end of the serial line is very unusual - just send your data and wait for the reply. By changing the low level states on the port manually you are likely to introduce transmission problems into the system.

    Try starting with the example code on the DataReceived event page (above) and you should have more reliable results.