LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

LabWindows GUI freezes with Arduino communications.

Solved!
Go to solution

Good morning,

I have a little issue with my LabWindows program, I will explain it in the following lines. 

I did a test bench with an Arduino Mega to perform 10 tests in a specific DUT and I control the entire system with a LabWindows GUI. Every single communication is ok, I send (from LabWindows to Arduino) and I receive (from Arduino to LabWindows) the correct value.

The problem is when Arduino makes test: if I want to stop the tests list in any time with LabWindows GUI (Command Button), I can't do it because the interface is freezed, so I can't push any button to click in case of emergency. I always wait the end of the tests to interact with the program.

 

HW parts:

- Arduino Mega;

- Cable TTL-234X-5V (with drivers installed);

- HP ZBook 15v G5.

 

SW used:

- Windows 10;

- Arduino IDE 1.8.9;

- LabWindows/CVI v17.0.0 (2017).

 

Configuration:

GND (Arduino Mega) --> Black wire GND (TTL-234X-5V);

Digital19 RX1 (Arduino Mega) --> Orange wire TXD (TTL-234X-5V);

Digital18 TX1 (Arduino Mega) --> Yellow wire RXD (TTL-234X-5V).

 

Windows S.O. recognizes the configuration above in "USB Serial Port (COM3)".

 

The code that I used is the following (I simplified it):

COMMAND BUTTON to start tests list

int CVICALLBACK testListArduino (int panel, int control, int event,
					void *callbackData, int eventData1, int eventData2)
{
	switch (event)
	{
		case EVENT_LEFT_CLICK:
			iTest = 1;
			do
			{
				if(iTest == 1)
				{
					firstTest(panel, info);
					if(iRS232Error != 0)
						iTest = 11;
					else
						iTest = 2;
				}
				else if(iTest == 2)
				{
					secondTest(panel, info);
					if(iRS232Error != 0)
						iTest = 11;
					else
						iTest = 3;
				}
				...
				else if(iTest == 10)
				{
					tenTest(panel, info);
					iTest = 11;
				}
			}while(iTest < 11);
			break;
	}
	return 0;
}

FUNCTION TEST (for example first)

...
#define READTERM	10
int iPortArduino = 3;
...

void firstTest(int panel, char info[LENGTH_INFO])
{
	dResult[0] = 0;
	dResult[1] = 0;
	dResult[2] = 0;

	SetCtrlVal(panel, PanelMain_STRING, "A");					// Letter A in Arduino is the first test case 
	DisableBreakOnLibraryErrors ();
	iRS232Error = OpenComConfig (iPortArduino, "", 9600, 0, 8, 1, 512, 512);	// Open port COM
	EnableBreakOnLibraryErrors ();
	if(iRS232Error != 0)								// If the COM is incorrect
	{
		MessagePopup("Error","Error.");
		dResult[0] = -1;
		dResult[1] = -1;
		dResult[2] = -1;
		return;
	}
	SetComTime (iPortArduino, 10);
	SetCTSMode (iPortArduino, LWRS_HWHANDSHAKE_OFF);
	FlushInQ(iPortArduino);
	GetCtrlVal(panel, PanelMain_STRING, info);					// info = 'A'
	Delay(1);									// I used this to delay serial port
	ComWrt(iPortArduino, info, strlen(info));					// Send A to Arduino
	inqlen = GetInQLen (iPortArduino);
	bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM);			// Take result1 from arduino script with Serial1.println(...);
	CopyString (tbox_read_data, 0, read_data, 0, bytes_read);
	dResult[0] = atof(&tbox_read_data);
	bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM);			// Take result2 from arduino script with Serial1.println(...);
	CopyString (tbox_read_data, 0, read_data, 0, bytes_read);
	dResult[1] = atof(&tbox_read_data);
	bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM);			// Take result3 from arduino script with Serial1.println(...);
	CopyString (tbox_read_data, 0, read_data, 0, bytes_read);
	dResult[2] = atof(&tbox_read_data);
	FlushInQ(iPortArduino);
	CloseCom (iPortArduino);							// Close port COM

	return;
}

Do you have any ideas to create a stop button for emergency in CVI? Or, with this managing, is impossible (i.e. maybe OpenComConfig, SetComTime, CloseCom create a particular condition to freeze CVI)?

Thank you and have a nice day.

 

Best Regards,

Stefano

0 Kudos
Message 1 of 6
(3,178 Views)

Hello,

this problem does not depend on serial communications but on the design principle you are using instead.

 

If you look into your code, you have all instructions lined up one after the other with no room for the system to intercept UI events like a button press. This results in the system being freezed until the sequence terminates.

 

An alternative way to develop such a system can be to use a state machine built upon a timer: basically, on every timer tick you check for the machine state and operate accordingly:

int CVICALLBACK TimerCallback (int panel, int control, int event,
		void *callbackData, int eventData1, int eventData2)
{
	if (event != EVENT_TIMER_TICK) return 0;

	switch (iTest) {
		case 1:
			firstTest (panel, info);
			if (iRS232Error != 0)
				iTest = 11;
			else
				iTest = 2;
			break;
		case 2:
			//....
			// Add cases for other tests
			break;
		case 11:
			// terminate the sequence
			// dispose of allocated resources
			// process test results
			iTest = 0;
			break;
	}
return 0;
}

 

Now, for this to operate properly you need to develop individual tests code so that it executes fast (ideally a little less than the timer interval): your Delay (1) actually freezes the system as well, so you need to develop a timing that leaves room to system activities; something along this line:

int MyDelayWithEventProcessing (double time)
{
	double	t0 = Timer ();

	abort = 0;
	while (Timer () - t0 < time) {
		ProcessSystemEvents ();
		if (abort) break;
		Delay (0.02);
	}

	return abort;
}

'abort' must be a global variable that is set to 1 when you press the abort button; you need to test the return value of the function in your code and operate properly in case the user wants to cancel the test in progress.

 

NOTE This code is only an initial code framework for you to start with, it needs to be optimized for your actual situation and can be largely improved in several aspects.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 2 of 6
(3,143 Views)

Hi Roberto,

thanks for your reply.

I tried to modify sw design (I removed do-while cycle and delay command) but the problem persists.

In this case I can stop the list tests after one and before the next (it's a good start Smiley Happy) but not during.

Every test time is from 3 to 8 seconds and, with a breakpoint help, I noticed a particular behaviour of test bench: when I send character from LabWindows to Arduino, LabWindows waits a respond from Arduino (so the GUI freezes from 3 to 8 seconds depending of the test); the code portion is the following

 

ComWrt(iPortArduino, info, strlen(info));					// Send A to Arduino
inqlen = GetInQLen (iPortArduino); bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM); // Take result1 from arduino script with Serial1.println(...); CopyString (tbox_read_data, 0, read_data, 0, bytes_read); dResult[0] = atof(&tbox_read_data); bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM); // Take result2 from arduino script with Serial1.println(...); CopyString (tbox_read_data, 0, read_data, 0, bytes_read); dResult[1] = atof(&tbox_read_data); bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM); // Take result3 from arduino script with Serial1.println(...); CopyString (tbox_read_data, 0, read_data, 0, bytes_read); dResult[2] = atof(&tbox_read_data); FlushInQ(iPortArduino);

Substantially, until bytes_read not respond the GUI is freezed.

Any ideas about this?

Thank you and have a nice day.

 

Best Regards,

Stefano

0 Kudos
Message 3 of 6
(3,069 Views)
Solution
Accepted by Steppy

Well, basically this is by design: when you call ComRd to read from the serial port the system hangs until the desired characters are received or a timeout occurs. The timeout for serial communications is set by calling SetComTime function after opening the port, and it defaults to 5 seconds if I'm not wrong.

In your case, you could implement a responsive wait loop which can be aborted with these lines (very similar to the responsive wait I posted earlier as you can see):

int ComWaitWithEventProcessing (int comPort, int numChars, double time, int *timeout)
{
	double	t0 = Timer ();

	abort = 0; *timeout = 1;
	while (Timer () - t0 < time) {
		if (GetInQLen (comPort) >= numChars) { timeout = 0; break; }
		ProcessSystemEvents ();
		if (abort) break;
		Delay (0.02);
	}

	return abort;
}

When the function terminates you both know whether there had been a timeout on the serial line or an abort request from the operator: if none of them is present you can proceed to read the port.

 



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 4 of 6
(3,048 Views)

Hi Roberto,
thanks for your help.

My software, finally, is complete: your design is ok to capture events when Labwindows waits serial port answer (in this case Arduino). On your function, I removed the pointer *timeout because it caused an error; without *timeout, the function works fine. I would share the principle (semplified):

 

...
#define READTERM	10
int iPortArduino = 3;           // Serial Port Arduino
int iNumChars = 30;		// Number of characters sent from Arduino to Labwindows
int iTime = 10;			// Your set time port COM (10 s)
...

void firstTest(int panel, char info[LENGTH_INFO])
{
	dResult[0] = 0;
	dResult[1] = 0;
	dResult[2] = 0;

	SetCtrlVal(panel, PanelMain_STRING, "A");					// Letter A in Arduino is the first test case 
	DisableBreakOnLibraryErrors ();
	iRS232Error = OpenComConfig (iPortArduino, "", 9600, 0, 8, 1, 512, 512);	// Open port COM
	EnableBreakOnLibraryErrors ();
	if(iRS232Error != 0)								// If the COM is incorrect
	{
		MessagePopup("Error","Error RS232.");
		dResult[0] = -1;
		dResult[1] = -1;
		dResult[2] = -1;
		return;
	}
	SetCTSMode (iPortArduino, LWRS_HWHANDSHAKE_OFF);
	FlushInQ(iPortArduino);
	GetCtrlVal(panel, PanelMain_STRING, info);					// info = 'A'
	ComWrt(iPortArduino, info, strlen(info));					// Send A to Arduino
	inqlen = GetInQLen (iPortArduino);
	ComWaitWithEventProcessing(iPortArduino, iNumChars, iTime);
	bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM);			// Take result1 from arduino script with Serial1.println(...);
	CopyString (tbox_read_data, 0, read_data, 0, bytes_read);
	dResult[0] = atof(&tbox_read_data);
	bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM);			// Take result2 from arduino script with Serial1.println(...);
	CopyString (tbox_read_data, 0, read_data, 0, bytes_read);
	dResult[1] = atof(&tbox_read_data);
	bytes_read = ComRdTerm (iPortArduino, read_data, 10, READTERM);			// Take result3 from arduino script with Serial1.println(...);
	CopyString (tbox_read_data, 0, read_data, 0, bytes_read);
	dResult[2] = atof(&tbox_read_data);
	FlushInQ(iPortArduino);
	CloseCom (iPortArduino);							// Close port COM

	return;
}

...
int ComWaitWithEventProcessing(int iPortArduino, int iNumChars, double iTime)
{
	double t0 = Timer();
	iAbort = 0;
	while(Timer() - t0 < iTime)
	{
		if(GetInQLen(iPortArduino) >= iNumChars)
		{
			break;
		}
		ProcessSystemEvents();
		if(iAbort)
		{
			ComWrt(iPortArduino, "Z", 1); 						// With character Z, I stopped Arduino sketch
			MessagePopup("EXIT","Stop tests list.");
			break;
		}
		Delay(0.02);
	}
	return iAbort;
}
...

I didn't know function ProcessSystemEvents() until yesterday and I recommend this concept communication without blocked delays.

Grazie mille Smiley Happy.

 

Best regards,

Stefano

 

0 Kudos
Message 5 of 6
(3,026 Views)

Hello, I'm glad you solved your problems.

The error on the wait function is probably my fault, the correct line should read (note the highlight in red):

		if (GetInQLen (comPort) >= numChars) { *timeout = 0; break; }

Ciao



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 6 of 6
(2,994 Views)