mik3y / usb-serial-for-android

Android USB host serial driver library for CDC, FTDI, Arduino and other devices.
MIT License
4.91k stars 1.59k forks source link

Data loss with PL2303 and bulkTransfer() - test case and workarounds #94

Closed RogerHardiman closed 5 years ago

RogerHardiman commented 9 years ago

I have been experiencing data loss when using a couple of devices with the PL2303HXD. The data loss shows up in the USBSerialDriverTest application as well as in my own app. One device is a PL2303 HXD based USB serial cable from pluggable.com. The other device is a Junsd JS-9006P stopwatch with a USB data port (PL2303HXD chip in inside on main circuit board so no option to use a different usb serial cable)

With the HXD serial cable I am sending 1 byte every 200ms from a remote device to Android, 9600,8,N,1 This is enough for the Driver Test app to miss one byte of data in every 10 to 15 bytes. Device is a Galaxy S5 running 4.4.2. With the Stopwatch, all I can report is the device is 9600 baud but emits bytes much slower than this

I read various posts about data loss with bulkTransfer().

ASYNC READ FIX I noticed the Async read() code in the FTDI class and implemented this in the Prolific class. My test where I was outputting 1 byte every 200ms now works with the async code,. But receiving data from the stopwatch still shows a couple of bytes lost in a 800 byte block of data. So Async Read is not perfect on the HXD based chipset with a Galaxy S5.

50MS SLEEP FIX While I was investigating I also found a thread in the Physicaloid forum ( https://github.com/ksksue/PhysicaloidLibrary/issues/7 ) where they also had data loss from bulkTransfer(). They inserted 50ms sleep between each bulkTransfer().

I tried it and it worked and refined this slightly by recording the time after each bulkTransfer() returns, to make sure a minimum of 50ms has elapsed before we call it again.

           (previous_time is a long, initialized to -1)

           if (previous_time_ms != -1) {
                long elapsed_time_ms = (System.nanoTime()/1000000) - previous_time_ms;
                if (elapsed_time_ms < 50) { //50ms
                    try {
                        Thread.sleep(50 - elapsed_time_ms);
                    } catch (Throwable t) {
                        // do nothing
                    }
                }
            }

            int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
                    readAmt, timeoutMillis);

            previous_time_ms = System.nanoTime()/1000000; // record time bulkTransfer returned

Elsewhere I noticed the normal library code worked fine on a S3 running Android 4.3 This is all a bit flakey with hardware differences between S3 and S5. Not too happy about Thread.Sleep() but it gives me a workaround for my hardware.

kai-morich commented 5 years ago

bulktransfer can cause data loss, always use async read with 0d48ed04e7cf3ff83c8089d4dedf61f1ba7277be

e-leoni commented 3 years ago

Hi @kai-morich , we have the same problem with Cp21xxSerialDriver in a UnoPlatform project. Our Read method is this:

                lock (mReadBufferLock)
                {
                    var request = new UsbRequest();
                    try
                    {
                        request.Initialize(mConnection, mReadEndpoint);
                        var buf = Java.Nio.ByteBuffer.Wrap(dest);
                        if (!request.Queue(buf, dest.Length))
                            throw new IOException("Error queueing request.");

                        mUsbRequest = request;
                        var response = mConnection.RequestWait();

                        if (response == null)
                            throw new IOException("Null response");

                        int nread = buf.Position();
                        if (nread > 0)
                        {
                            buf.Position(0);
                            for (int i = 0; i < nread; i++)                            
                                dest[i] = (byte)buf.Get(i);

                            //Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
                            return nread;
                        }
                        else
                            return 0;
                    }
                    finally
                    {
                        mUsbRequest = null;
                        request.Close();
                    }
                }

It works fine at the beginning but after some reading, it loses data and we are unable to read from serial port. Do you have any idea to solve it? Thanks, emanuele

kai-morich commented 3 years ago

what is your dest size?

e-leoni commented 3 years ago

4096, but we have tried 1024 to without success.

kai-morich commented 3 years ago

Dest size looks ok, as it is a multiple of USB packet size 64. The original issue was fixed by switching from bulkTransfer to queue + requestwait, so your question looks like a different issue. Are you reading the data fast enough, e.g. like SerialInputOutputManager in a separate thread?

e-leoni commented 3 years ago

Yes, at the beginning data is correct. After requests made by write commands, we have data loss.