1

Topic: Generic HID: EventHandler for incoming data?

Hello,

I'm currently trying to write a .NET Winforms application to exchange some configuration info with microcontroller (and in the future, probably do logging and other data exchange as well).  The device enumerates in windows, and I am able to find it in my application and open the HID stream.  I'm also able to send data to the device.  Where I'm running into problems is receiving data from the device.

My device is a generic HID with 2 Endpoints:
EP1: 64-Bytes INT, 20mS
EP2: 64-Bytes, INT, 20mS

My code to find and open the device is below, both upon loading of the form, and whenever the device list changes:

Load:

/*On form load, check for our device and set up the listChanged event*/
        private void Form1_Load(object sender, EventArgs e)
        {

            var hidDeviceList = list.GetHidDevices().ToArray();

            deviceFound = false;
            foreach (HidDevice dev in hidDeviceList)
            {

                if ((dev.VendorID == 0x0000) && (dev.ProductID == 0x0505))
                {
                    device = dev;

                    /*We found our device*/
                    if (device.TryOpen(out hidStream))
                    {

                        using (hidStream)
                        {
                            
                            deviceFound = true;

                        }

                    }

                }

            }

            if (deviceFound)
            {
                lbl_connStatus.Text = "CONNECTED";

                reportDescriptor = device.GetReportDescriptor();
                inputReceiver = reportDescriptor.CreateHidDeviceInputReceiver();
                deviceItem = reportDescriptor.DeviceItems[0];
                inputParser = deviceItem.CreateDeviceItemInputParser();
                list.Changed += new EventHandler<DeviceListChangedEventArgs>(DeviceListChanged);
                inputReceiver.Received += new EventHandler(ReceivedData);
                inputReceiver.Start(hidStream);
            }
            else
            {
                lbl_connStatus.Text = "NOT FOUND";
            }

        }

DeviceListChanged Event:

/*On any change to list of connected devices, scan for our device*/
/*if not present, unregister ReceivedData event handler*/
        private void DeviceListChanged(object sender, EventArgs e)
        {

            var hidDeviceList = list.GetHidDevices().ToArray();

            deviceFound = false;
            foreach (HidDevice dev in hidDeviceList)
            {
                if ((dev.VendorID == 0x0000) && (dev.ProductID == 0x0505))
                {
                    device = dev;
                    /*We found our interface*/
                    if (device.TryOpen(out hidStream))
                    {
                        deviceFound = true;
                    }
                    
                }

            }

            if (deviceFound)
            {
                lbl_connStatus.Invoke((MethodInvoker) delegate
                {
                    lbl_connStatus.Text = "CONNECTED";
                });
                using (hidStream)
                {
                    reportDescriptor = device.GetReportDescriptor();
                    inputReceiver = reportDescriptor.CreateHidDeviceInputReceiver();
                    deviceItem = reportDescriptor.DeviceItems[0];
                    inputParser = deviceItem.CreateDeviceItemInputParser();
                    inputReceiver.Start(hidStream);
                    inputReceiver.Received += new EventHandler(ReceivedData);
                }

            }
            else
            {
                inputReceiver.Received -= ReceivedData;
                lbl_connStatus.Invoke((MethodInvoker)delegate
                {
                    lbl_connStatus.Text = "NOT FOUND";
                });
            }

        }

DataReceived Event Handler

        private void ReceivedData(object sender, EventArgs e)
        {
            HidSharp.Reports.Report report;

            if(inputReceiver.TryRead(inputReportBuffer,0,out report))
            {

                if (inputParser.TryParseReport(inputReportBuffer, 0, report))
                {
                    /*TODO: Read the data here, send to main thread*/

                }

            }

        }

I was under the impression that this would fire my ReceivedData event when I get data in my IN endpoint, but it does not fire (I never hit a breakpoint placed inside).  Does inputReceiver.Received not do what I'm expecting it to do perhaps?  Maybe I'm set up fine and it's the microcontroller code I need to look deeper at?

I based my DataReceived handler off this snippet in the example code:

    inputReceiver.Received += (sender, e) =>
    {
         Report report;
         while (inputReceiver.TryRead(inputReportBuffer, 0, out report))
         {
              // Parse the report if possible.
              // This will return false if (for example) the report applies to a different DeviceItem.
              if (inputParser.TryParseReport(inputReportBuffer, 0, report))
              {
                  // If you are using Windows Forms, you could call BeginInvoke here to marshal the results
                  // to your main thread.
                  WriteDeviceItemInputParserResult(inputParser);
              }
          }
      };

Thanks in advance for the help!

2 (edited by KTrenholm 2019-08-12 15:33:58)

Re: Generic HID: EventHandler for incoming data?

OK I think I've got it sorted out, it was at the end of the day a pretty dumb problem entirely on my end with the microcontroller firmware.

I tried using the HidStream.BeginRead() and ASyncCallback method of reading and while the callback would fire, it was only because the timeout was elapsing.  I was still getting no bytes on EP1.  While perusing the code on both ends I had a real facedesk moment when it jumped out at me in my firmware:

It was an issue with the the length parameter in the function to load EP1 .  I'm doing 64 Bytes each way, and my IN Endpoint buffer on the microcontroller side I was trying to load 65 bytes rather than 64 into EP1.  Since I was just passing this info into an API function that doesn't have a return status, I'm guessing it just didn't exchange data on this endpoint when I passed it a bad length parameter.  Either that or maybe something was getting clobbered in the overrun, I need to have a deeper look into why this failed in the way that it did.

I think in writing my code I just put 65 bytes because in my winforms application my buffers have to be 65-bytes long (since the interface # is always the leading byte, followed by the data bytes).

As soon as I got that squared away, reading the IN Endpoint worked immediately.  Both using the InputReceiver, as well as using HidStream.BeginRead() and IAsyncCallback.