How solid is the Mono SerialPort class?

I have an application that, among other things, uses the SerialPort to communicate with a Digi XBee coordinator radio.

The code for this works rock solid on the desktop under .NET.

Under Mono running on a Quark board and WindRiver Linux, I get about a 99% failure rate when attempting to receive and decode messages from other radios in the network due to checksum validation errors.

Things I have tested:

  1. I'm using polling for the serial port, not events, since event-driven serial is not supported in Mono. So the problem is not event related.
  2. The default USB Coordinator uses an FTDI chipset, but I swapped out to use a proto board and a Prolific USB to serial converter and I see the same failure rate. I think this eliminates the FTDI driver as the problem.
  3. I changed the code to never try duplex communication. It's either sending or receiving. Same errors.
  4. I changed the code to read one byte at a time instead of in blocks sized by the size identifier in the incoming packet. Same errors.
  5. I see this with a variety of remote devices (smart plug, wall router, LTH), so it's not remote-device specific.
  6. The error occurs with solicited or unsolicited messages coming from other devices.
  7. I looked at some of the raw packets that fail a checksum and manual calculation gets the same result, so the checksum calculation itself is right.
  8. Looking at the data I see what appear to be packet headers mid-packet (i.e. inside the length indicated in the packet header). This makes me think that I'm "missing" some bytes, causing subsequent packet data to be getting read into earlier packets.

Again, this works fine on the desktop, but for completeness, this is the core of the receiver code (with error checking removed for brevity):

do
{
    byte[] buffer;

    // find the packet start
    byte @byte = 0;

    do
    {
        @byte = (byte)m_port.ReadByte();
    } while (@byte != PACKET_DELIMITER);

    int read = 0;

    while(read < 2)
    {
        read += m_port.Read(lengthBuffer, read, 2 - read);
    }

    var length = lengthBuffer.NetworkToHostUShort(0);

    // get the packet data
    buffer = new byte[length + 4];

    buffer[0] = PACKET_DELIMITER;
    buffer[1] = lengthBuffer[0];
    buffer[2] = lengthBuffer[1];

    do
    {
       read += m_port.Read(buffer, 3 + read, (buffer.Length - 3) - read);
    } while (read < (length + 1));

    m_frameQueue.Enqueue(buffer);
    m_frameReadyEvent.Set();
} while (m_port.BytesToRead > 0);

I can only think of two places where the failure might be happening - the Mono SerialPort implementation or the WindRiver serial port driver that's sitting above the USB stack. I'm inclined to think that WindRiver has a good driver.

To add to the confusion, we're running Modbus Serial on the same device (in a different application) via Mono and that works fine for days, which somewhat vindicates Mono.

Has anyone else got any experience with the Mono SerialPort? Is it solid? Flaky? Any ideas on what could be going on here?

Answers

    m_port.Read(lengthBuffer, 0, 2);

That's a bug, you have no guarantee whatsoever that you'll actually read two bytes. Getting just one is very common, serial ports are slow. You must use the return value of Read() to check. Note how you did it right in your second usage. Beyond looping, the simple alternative is to just call ReadByte() twice.

Posted on by Hans Passant

Relevant tags