1 (edited by Mr.X 2018-01-14 09:57:24)

Topic: HIDSharp ASync Stream Reading

First of all, thank you for making this amazing library, and for putting it out there. Looks extremely professional and well designed!

I'm new to HID communication, but not to programming and hardware communication (done quite a bit of Serial Port and MIDI stuff in .NET)

I'm trying to figure out the "proper" way to do communication with my HID device. What I have made through trial-and-error works, but I'm sure it's not "correct".

Here's a sample of my code:

        HidSharp.HidDevice USBDevice;
        HidSharp.DeviceList USBList;
        HidSharp.HidStream USBStream;
        IAsyncResult result;

    
        byte[] testUSBHeader = new byte[] { 01, 14, 0x0a, 0, 0x0b, 0, 0x32, 0, 2, 0, 1, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] USBBuffer;

        private void btnConnect_Click(object sender, EventArgs e)
        {
            USBList = DeviceList.Local;
            USBDevice = USBList.GetHidDevices(0x0483,0xA0E0).First();
            if (USBDevice != null && USBDevice.TryOpen(out USBStream))
            {
                USBStream.ReadTimeout = 32767;
                textBox1.Text = USBDevice.DevicePath;
                btnConnect.Text = "Connected!";
                USBBuffer = new byte[65];
                result = USBStream.BeginRead(USBBuffer, 0, 64, new AsyncCallback(BytesReady), null);
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            byte[] BytesToSend = new byte[65];
            Array.Copy(testUSBHeader, 0, BytesToSend, 1, testUSBHeader.Length);
            BytesToSend[0] = 0;
            USBStream.Write(BytesToSend, 0, 65);
        }

        private void BytesReady(IAsyncResult result)
        {
            try
            {
                Debug.WriteLine($"Bytes Read = {USBStream.EndRead(result)}");

                if (listBox1.InvokeRequired)
                    listBox1.BeginInvoke((MethodInvoker)delegate () { listBox1.Items.Add(BitConverter.ToString(USBBuffer)); ; });
                else
                    listBox1.Items.Add(BitConverter.ToString(USBBuffer));

                result = USBStream.BeginRead(USBBuffer, 0, 64, new AsyncCallback(BytesReady), null);

            }
            catch (Exception)
            {
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            USBStream.EndRead(result);
            USBStream.Dispose();
        }
    }

Some things I don't quite understand:
Why do I need to call BeginRead inside BytesReady? It seems if I don't, no more reads happen. I thought BeginRead would continue until Cancelled. I've tried it with and without EndRead, it only reads once unless I call it again.

How do I close the stream and stop reading? When I call USBStream.Close() or USBStream.Dispose(), it calls BytesReady continuously. The only way I can get it to stop is with the try/catch on EndRead, so it doesn't call BeginRead again.

What is a better way to do what I'm trying to do?

Thank you for your help and advice!

2

Re: HIDSharp ASync Stream Reading

Thanks for your comments about HIDSharp. I use it in the software I develop for my daily bread, so it gets a lot of testing. You can rely on it. smile

Edit: ...Actually, the serial port support is new and not yet reliable. The HID support is solid. Anyway!

As for BeginRead, it pends a read, and when it completes you get a call in EndRead (or, if you prefer polling or timers, you *can* check IsCompleted from the original thread and not even provide a callback, or you can use the AsyncWaitHandle). Operating systems buffer up HID packets, so you won't lose any unless you wait too long. (Windows, for instance, will buffer up 512 packets on XP and newer.)

When the device is disconnected, it should throw IOException. When you close the stream, it should throw ObjectDisposedException. If you are using asynchronous calls, this may throw in BeginRead if you called Close before calling BeginRead, or it will be routed to EndRead if you called Close after calling BeginRead.

Four approaches off the top of my head...

(1) If you want to use a timer or loop in your main thread (a game might do this), store IAsyncResult (exception handling left out in this example):
if (this.ar != null && this.ar.IsCompleted) { int bytes = hid.EndRead(...); do stuff; this.ar = null; }
if (this.ar == null) { this.ar = hid.BeginRead(...); }

(2) Using BeginRead/EndRead:
Have a KeepReading function which does all the BeginRead work. Call it from the main thread after opening the stream, and call it after EndRead processing in the worker thread to loop.

(3) Spawn a thread and use blocking calls in it, with timeouts if need be. You can always Close() from your main thread to interrupt the blocking call. For cases where the interaction with the device is not simple, this is the approach I take.

(4) Spawn a thread and use BeginRead and the IAsyncResult's AsyncWaitHandle. This is a lot like (3) except you can wait for other events as well, or wait on a timeout. For example:
while (true) {
  if (this.ar != null) { this.ar.AsyncWaitHandle.WaitOne(100); }
  if (this.ar != null && this.ar.IsCompleted) { int bytes = hid.EndRead(...); do stuff; this.ar = null; }
  if (this.ar == null) { this.ar = hid.BeginRead(...); }
  // Do other processing in the separate thread which must be done at at least 10 Hz. The read will keep going.
}

Hope this helps,

James

3 (edited by Mr.X 2018-03-15 12:31:10)

Re: HIDSharp ASync Stream Reading

Thank you for your reply.

I've read what you wrote many times, but I don't really understand it.

I was hoping to figure out how to not get stuck in a loop when closing the device or when it disconnects, and I'm sure you're addressing that issue, but I don't really understand.

No matter, my try/catch method seems to work for now.

4

Re: HIDSharp ASync Stream Reading

Exceptions are the appropriate way, yes. If the device is closed or disconnected, either BeginRead or EndRead will throw an exception. Don't call BeginRead again in that case.

5

Re: HIDSharp ASync Stream Reading

Thank you!