Click here to Skip to main content
16,013,605 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello everyone,
I try to develop an application that can communicate with HID device. I can send and receive data. But when receive my program stucks which means continue to send and receive data but other functions like changing label text, closing program, drawing graph etc wont work. Communication starts when I click receive button. Could anyone help me?

What I have tried:

C#
<pre>using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CyUSB;

namespace EGI
{
    public partial class EGI_Main : Form
    {
        // *********************** START OF PROGRAM MAIN ****************************** //

        USBDeviceList usbDevices = null;            // Pointer to list of USB devices
        CyHidDevice myHidDevice = null;             // Handle of USB device

        int VID = 0x04B4;                           // Cypress Vendor ID
        int PID = 0xE177;                           // Example Project Product ID

        bool communicate = false;                   // Communication status

        byte que = 00;                              // Command Byte
        byte PGAValue = 02;                         // PGA
        byte[] receivedDatas = new byte[128];       // Byte array to store 128 bytes of received data from device

        /**********************************************************************
        * NAME: EGI - Ekin Generic HID Communication Interface
        *
        * DESCRIPTION: Main function called initially upon the starting of the
        * application. Used to un-initialized variables, the GUI application, register
        * the event handlers, and check for a connected device.
        *
        ***********************************************************************/

        public EGI_Main()
        {
            InitializeComponent();
            // Create a list of CYUSB devices for this application
            usbDevices = new USBDeviceList(CyConst.DEVICES_HID);
            //Add event handlers for device attachment and device removal
            usbDevices.DeviceAttached += new EventHandler(usbDevices_DeviceAttached);
            usbDevices.DeviceRemoved += new EventHandler(usbDevices_DeviceRemoved);
            //Connect to the USB device
            GetDevice();
        }

        /**********************************************************************
        * NAME: EGI_Main_Load
        *
        * DESCRIPTION: Loads and sets necessary companents and settings on start.
        *
        ***********************************************************************/

        private void EGI_Main_Load(object sender, EventArgs e)
        {
            // Real data graph settings
            chartResult.ChartAreas[0].AxisX.ScaleView.Zoom(0, 127);
            chartResult.ChartAreas[0].AxisY.ScaleView.Zoom(0, 255);
            chartResult.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
            chartResult.Series[0].Color = Color.Black;
            chartResult.Series[0].BorderWidth = 3;

            // On line average result graph settings
            chartResult.Series.Add("onLineAvg");
            chartResult.Series["onLineAvg"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
            chartResult.Series["onLineAvg"].Color = Color.Red;
            chartResult.Series["onLineAvg"].BorderWidth = 3;

            // Multipline with on line average result graph settings
            chartResult.Series.Add("multOnLineAvg");
            chartResult.Series["multOnLineAvg"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
            chartResult.Series["multOnLineAvg"].Color = Color.Navy;
            chartResult.Series["multOnLineAvg"].BorderWidth = 3;

            // Exceed point(s) graph settings
            chartResult.Series.Add("exceed");
            chartResult.Series["exceed"].Color = Color.Brown;
            chartResult.Series["exceed"].BorderWidth = 3;
        }

        /**********************************************************************
        * NAME: usbDevices_DeviceRemoved
        *
        * DESCRIPTION: Event handler for the removal of a USB device. When the removal
        * of a USB device is detected, this function will be called which will check to
        * see if the device removed was the device we were using. If so, then reset
        * device handler (myHidDevice), disable the timer, and update the GUI.
        *
        ***********************************************************************/

        public void usbDevices_DeviceRemoved(object sender, EventArgs e)
        {
            USBEventArgs usbEvent = e as USBEventArgs;
            if ((usbEvent.ProductID == PID) && (usbEvent.VendorID == VID))
            {
                InputTimer.Enabled = false; // Disable interrupts for polling HID
                myHidDevice = null;
                GetDevice(); // Process device status
            }
        }

        /**********************************************************************
        * NAME: usbDevices_DeviceAttached
        *
        * DESCRIPTION: Event handler for the attachment of a USB device. The function
        * first checks to see if a matching device is already connected by seeing
        * if the handler (myHidDevice) is null. If no device is previously attached,
        * the function will call GetDevice to check and see if a matching device was
        * attached.
        *
        ***********************************************************************/

        public void usbDevices_DeviceAttached(object sender, EventArgs e)
        {
            if (myHidDevice == null)
            {
                GetDevice(); // Process device status
            }
        }

        /**********************************************************************
        * NAME: GetDevice
        *
        * DESCRIPTION: Function checks to see if a matching USB device is attached
        * based on the VID and PID provided in the application. When a device is
        * found, it is assigned a handler (myHidDevice) and the GUI is updated to
        * reflect the connection. Additionally, if the device is not connected,
        * the function will update the GUI to reflect the disconnection.
        *
        ***********************************************************************/
        public void GetDevice()
        {
            //Look for device matching VID/PID
            myHidDevice = usbDevices[VID, PID] as CyHidDevice;
            if (myHidDevice != null) //Check to see if device is already connected
            {
                Status.Text = "Connected";
                Status.ForeColor = Color.Green;
                InputTimer.Enabled = true; //Enable background timer
            }
            else
            {
                Status.Text = "Disconnected";
                Status.ForeColor = Color.Red;
            }
        }
        
        /**********************************************************************
        * NAME: Set_VidPid_Click
        *
        * DESCRIPTION: Updates the applications Vendor ID and Product ID based on
        * user input when the "Set" button is clicked. This will cause the default VID
        * and PID of 0x04B4 and 0xE177 to be overwritten. The function will then
        * call GetDevice() to check for matching USB device.
        *
        ***********************************************************************/
        private void Set_VidPid_Click(object sender, EventArgs e)
        {
            //Respond to update of VID and PID value by pressing the "Set" button
            VID = Convert.ToInt32(VidTextBox.Text, 16);
            PID = Convert.ToInt32(PidTextBox.Text, 16);
            GetDevice();
        }

        /**********************************************************************
        * NAME: receive_Click
        *
        * DESCRIPTION: Starts and stops communication with device.
        *
        ***********************************************************************/

        private void receive_Click(object sender, EventArgs e)
        {
            if (communicate)
            {
                communicate = false;
                receive.Text = "Communicate";
                receive.BackColor = Color.White;
                receive.ForeColor = Color.Black;
            }
            else
            {
                //not working
                communicate = true;
                receive.Text = "Stop";
                receive.BackColor = Color.Red;
                receive.ForeColor = Color.White;
                que = 00;
                //working
                communicateNow();
            }
        }

        /**********************************************************************
        * NAME: communicateNow
        *
        * DESCRIPTION:Sends commands to device.
        *
        ***********************************************************************/

        private void communicateNow()
        {
            if (communicate)
            {
                // Load data into Output Buffer
                myHidDevice.Outputs.DataBuf[0] = 00;                                    // Report ID
                myHidDevice.Outputs.DataBuf[1] = (byte)numericUpDownThreshold.Value;    // Threshold
                myHidDevice.Outputs.DataBuf[2] = (byte)numericUpDownPB.Value;           // PB
                myHidDevice.Outputs.DataBuf[3] = PGAValue;                              // PGA
                myHidDevice.Outputs.DataBuf[4] = 87;                                    // W/R
                myHidDevice.Outputs.DataBuf[5] = que;                                   // Command byte
                myHidDevice.Outputs.DataBuf[6] = 00;
                myHidDevice.Outputs.DataBuf[7] = 00;
                myHidDevice.Outputs.DataBuf[8] = 00;
                myHidDevice.Outputs.DataBuf[9] = 00;

                // Function call to send data to device
                myHidDevice.WriteOutput();
                // Function call to receive data from device
                myHidDevice.ReadInput();

                byte[] tempData = myHidDevice.Inputs.DataBuf;

                for (int i = 2; i < 34; i++)
                {
                    if (que == 0)
                        receivedDatas[i - 2] = tempData[i - 2];
                    else if (que == 1)
                        receivedDatas[32 + i - 2] = tempData[i - 2];
                    else if (que == 2)
                        receivedDatas[64 + i - 2] = tempData[i - 2];
                    else if (que == 3)
                        receivedDatas[96 + i - 2] = tempData[i - 2];
                }

                if (que < 03)
                {
                    que++;
                    communicateNow();
                }
                else
                {
                    que = 00;
                    dataProcess(receivedDatas);
                }
            }
        }
        

        /**********************************************************************
        * NAME: dataProcess
        *
        * DESCRIPTION: Regroup the datas, writes them to specific textboxes and
        * calls calculateAndDrawGraph to calculate and draw graph.
        *
        ***********************************************************************/

        private void dataProcess(byte[] realDatas)
        {
            bool highLightPower = false;
            bool lowLightPower = false;

            // Clear textboxes
            receivedData_00.Text = null;
            receivedData_01.Text = null;
            receivedData_02.Text = null;
            receivedData_03.Text = null;

            // Count datas to separate to textboxes
            int countOfDatas = 0;

            // Write all datas to specified textboxes
            foreach (byte r in realDatas)
            {
                if (countOfDatas < 32)
                    receivedData_00.AppendText(r + " - ");
                else if (countOfDatas < 64)
                    receivedData_01.AppendText(r + " - ");
                else if (countOfDatas < 96)
                    receivedData_02.AppendText(r + " - ");
                else if (countOfDatas < 128)
                    receivedData_03.AppendText(r + " - ");

                // Check whether light power is high or low.
                if (r >= 190)
                    highLightPower = true;
                else if (r <= 20)
                    lowLightPower = false;

                countOfDatas++;
            }

            //not working
            if (highLightPower)
            {
                labelLightPower.BackColor = Color.Red;
                labelLightPower.ForeColor = Color.White;
                labelLightPower.Text = "Decrease";
            }
            else if (lowLightPower)
            {
                labelLightPower.BackColor = Color.Yellow;
                labelLightPower.ForeColor = Color.Black;
                labelLightPower.Text = "Increase";
            }
            else
            {
                labelLightPower.BackColor = Color.Green;
                labelLightPower.ForeColor = Color.White;
                labelLightPower.Text = "";
            }

            calculateAndDrawGraph(realDatas);
        }

        /**********************************************************************
        * NAME: calculateAndDrawGraph
        *
        * DESCRIPTION: Calculates averages and draws their graphs. Recall 
        * communicateNow() to receive new datas.
        *
        ***********************************************************************/

        private void calculateAndDrawGraph(byte[] realDatas)
        {
            //not working
            chartResult.Series[0].Points.Clear();
            for(int i = 0; i < 128; i++)
            {
                chartResult.Series[0].Points.AddXY(i, realDatas[i]);
            }
            //working
            communicateNow();
        }

        /**********************************************************************
        * NAME: radioButton_CheckedChanged
        *
        * DESCRIPTION: Sets PGAValue.
        *
        ***********************************************************************/
        //cannot click radio buttons
        private void radioButton_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton1.Checked)
                PGAValue = 01;
            else if (radioButton2.Checked)
                PGAValue = 02;
            else if (radioButton4.Checked)
                PGAValue = 04;
            else if (radioButton8.Checked)
                PGAValue = 08;
        }

    }
}
Posted
Updated 3-Jan-18 21:39pm

1 solution

Start by looking at your WriteOutput and (more importantly) ReadInput methods: If these wait for a response - and I assume that the read one at least does - then it is a blocking call, which means that your thread is stuck waiting for the remote device to respond. While that is happening, your UI thread is stuck, and cannot do any updates to your display (or anything else, such as respond to mouse or keyboard input).

The only solution to that is a complete redesign, using a separate thread for communications which relays information to the UI thread to draw it's graphs and so on. You may find the BackgroundWorker class suitable for this as it includes a simple event driven mechanism for talking back to the UI task via Progress indication (which can include a user object to hold your data).
 
Share this answer
 
Comments
alidayan 4-Jan-18 4:32am    
Thank you for your response. Yes device waits command to response so I have to send some data with write command and wait for response from device with read method. I will try BackgroundWorker class which I have never try. Also if I comment communicateNow() Program is not going to stuck but every time that I want to get data from device must click on receive button.
OriginalGriff 4-Jan-18 4:44am    
It's a pretty simple class to use:
https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
Includes a basic example.
alidayan 4-Jan-18 6:25am    
Thank you very much. I have added
private void backgroundWorkerGetAndProcessData_DoWork(object sender, DoWorkEventArgs e)        {            communicateNow();        }
and called with
backgroundWorkerGetAndProcessData.RunWorkerAsync();
when RunWorkerCompleted recalled it with
private void backgroundWorkerGetAndProcessData_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)        {            backgroundWorkerGetAndProcessData.RunWorkerAsync();        }
now get System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.' error
OriginalGriff 4-Jan-18 6:42am    
That's why I said it needs a complete redesign: you can't access UI controls from any thread other than the UI thread, and the standard .NET controls aren;t thread safe. You can't just throw all the code into a different thread and hope it works, you need to think about what needs to be done in which thread and how the two threads need to communicate with each other, and when.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900