TwinCAT 3 FOR LOOP

Vijolica555

Member
Join Date
Apr 2018
Location
Germany
Posts
21
Hello guys,

I am using TwinCAT 3 and communication with sensor with RS485 Protokol using SendString and ReceiveString FBs https://infosys.beckhoff.com/englis...erial_communication/9007204546041867.html&id=). So i trigger send command and then receive data fb in order to get response. I am communicating for a while now and we are getting data out of sensor successfully. Circumstances changed and we need some more info out of the sensor, which means that i need to send 200 different command to the sensor in cycle. I tried to use FOR loop in two ways but unsuccessfully. I would appreciate if anybody has an Idea how to make it working.

Please look step 4. I tried to do for loop for 200 commands. For each command we need to do the same steps as for RST command: so nStep0 - we choose command, nStep1 - we calculate the checksum, nStep 2 - sendString and nStep 3- receive string.

Example I with FOR loop (i would like that whole process under FOR is done - i want to wait fbSendString255 is not Busy, write/save the values and then do next command), Example II with CASE inside of FOR loop (here is program overloaded).

Example I:
Code:
CASE nStep OF
	

	0: 
		sRequest := 'RST'; //We sent RST command and reset the sensor
		nStep := 1;
	1: //CheckSum
		fbCheckSum(sSendString := sRequest); //we calculate checksum of the command
		nStep := 2;
	2: //Send RST Command
			sSendString := sRequest;
			CheckSum := fbCheckSum.Checksum;
			fbSendString255.SendString := concat(sSendString, Checksum);
			fbSendString255.SendString := concat(fbSendString255.SendString, '$0D');
			fbSendString255(TxBuffer := GVL_Gepa.TxBuffer);
			bWait := TRUE;
		END_IF
		IF NOT fbSendString255.Busy THEN //We wait that send string finished
			nStep := 3;
		END_IF
	3: 
		fbReceiveString255(
			Prefix			:= , 
			Suffix			:= '$0D', 
			Timeout			:= T#5MS, 
			Reset			:= , 
			StringReceived	=> bStringReceived, 
			Busy			=> bReceiveBusy , 
			Error			=> eReceiveErrorID, 
			RxTimeout		=> bReceiveTimeout, 
			ReceivedString	:= sReceivedStringRST, 
			RXbuffer		:= GVL_Gepa.RxBuffer
		);

		tWaitRST(
			IN:= TRUE, 
			PT:= T#10MS);
		
		sCheck := LEFT(sReceivedStringRST,2);
			
		IF sCheck ='OK' AND NOT fbReceiveString255.Busy AND tWaitRST.Q AND  ((nMinutes = 0 AND nSeconds = 0) OR 
																			(nMinutes = 10  AND nSeconds = 0) OR
																			(nMinutes = 20  AND nSeconds = 0) OR 
																			(nMinutes = 30  AND nSeconds = 0)OR 
																			(nMinutes = 40  AND nSeconds = 0)OR 
																			(nMinutes = 50  AND nSeconds = 0))																															
																											 THEN
			tWaitRST(IN := FALSE,PT := T#10MS);			
			nStep := 4;
			ELSE
				nStep := 0;
		END_IF
		
	4:
		FOR nRequest := 0 TO 200 DO

				sRequest := arrRequest[nRequest];
				GVL_Gepa.bSave := FALSE; //reset save value
				
                                fbCheckSum(sSendString	:= sRequest );
	
				sSendString := sRequest;
				CheckSum := fbCheckSum.Checksum;
				fbSendString255.SendString := concat(sSendString, Checksum);
				fbSendString255.SendString := concat(fbSendString255.SendString, '$0D');
				fbSendString255(TxBuffer := GVL_Gepa.TxBuffer);	
				IF NOT fbSendString255.Busy THEN 
					fbReceiveString255(
						Prefix			:= , 
						Suffix			:= '$0D', 
						Timeout			:= T#5MS, 
						Reset			:= , 
						StringReceived	=> bStringReceived, 
						Busy			=> bReceiveBusy , 
						Error			=> eReceiveErrorID, 
						RxTimeout		=> bReceiveTimeout, 
						ReceivedString	:= sReceivedString, 
						RXbuffer		:= GVL_Gepa.RxBuffer);
						
					sCheck := LEFT(sReceivedString,2); 
					tWait(IN	:= TRUE,PT	:= T#10MS);
					IF tWait.Q AND NOT fbReceiveString255.Busy AND sCheck ='OK'  THEN
						tWait(IN := FALSE,PT := T#10MS);
						GVL_Gepa.bSave := TRUE;
						ELSE
						nStep := 0;
						GVL_Gepa.bSave := FALSE;
					END_IF
				END_IF
				
			IF nRequest = 201 THEN
				nStep := 0;
			END_IF
		END_FOR
		//nStep := 0;
END_CASE

Example II:
Code:
	0: // RST request
		
		sRequest := 'RST';
		nStep := 1;
	1: //CheckSum
		fbCheckSum(sSendString := sRequest);
		nStep := 2;
	2: //Send RST Command
			sSendString := sRequest;
			CheckSum := fbCheckSum.Checksum;
			fbSendString255.SendString := concat(sSendString, Checksum);
			fbSendString255.SendString := concat(fbSendString255.SendString, '$0D');
			fbSendString255(TxBuffer := GVL_Gepa.TxBuffer);
		END_IF
		IF NOT fbSendString255.Busy THEN 
			nStep := 3;
		END_IF
	3: 
		fbReceiveString255(
			Prefix			:= , 
			Suffix			:= '$0D', 
			Timeout			:= T#5MS, 
			Reset			:= , 
			StringReceived	=> bStringReceived, 
			Busy			=> bReceiveBusy , 
			Error			=> eReceiveErrorID, 
			RxTimeout		=> bReceiveTimeout, 
			ReceivedString	:= sReceivedStringRST, 
			RXbuffer		:= GVL_Gepa.RxBuffer
		);

		tWaitRST(
			IN:= TRUE, 
			PT:= T#10MS);
		
		sCheck := LEFT(sReceivedStringRST,2);
			
		IF sCheck ='OK' AND NOT fbReceiveString255.Busy AND tWaitRST.Q AND  ((nMinutes = 0 AND nSeconds = 0) OR 
																			(nMinutes = 10  AND nSeconds = 0) OR
																			(nMinutes = 20  AND nSeconds = 0) OR 
																			(nMinutes = 30  AND nSeconds = 0)OR 
																			(nMinutes = 40  AND nSeconds = 0)OR 
																			(nMinutes = 50  AND nSeconds = 0))																															
																											 THEN
			tWaitRST(IN := FALSE,PT := T#10MS);			
			nStep := 4;
	
		END_IF
		
	4:
		FOR nRequest := 0 TO 200 DO
			nRequestStep := 0; //reset case
			WHILE nRequestStep <9 DO
			CASE nRequestStep OF
				0: 
					sRequest := arrRequest[nRequest];
					GVL_Gepa.bSave := FALSE; //reset save value
					nRequestStep := 1;
				1: 
					fbCheckSum(sSendString	:= sRequest );
					nRequestStep := 2;
				2:
					sSendString := sRequest;
					CheckSum := fbCheckSum.Checksum;
					fbSendString255.SendString := concat(sSendString, Checksum);
					fbSendString255.SendString := concat(fbSendString255.SendString, '$0D');
					fbSendString255(TxBuffer := GVL_Gepa.TxBuffer);	
					IF NOT fbSendString255.Busy THEN //SentString finished go on
						nRequestStep := 3;
					END_IF
				3: 
					fbReceiveString255(
						Prefix			:= , 
						Suffix			:= '$0D', 
						Timeout			:= T#5MS, 
						Reset			:= , 
						StringReceived	=> bStringReceived, 
						Busy			=> bReceiveBusy , 
						Error			=> eReceiveErrorID, 
						RxTimeout		=> bReceiveTimeout, 
						ReceivedString	:= sReceivedString, 
						RXbuffer		:= GVL_Gepa.RxBuffer);

					sCheck := LEFT(sReceivedString,2); //OK or ER.. When ER check sMessage and do what needed: restart  ect
					tWait(IN	:= TRUE,PT	:= T#10MS);
					IF tWait.Q AND NOT fbReceiveString255.Busy AND sCheck ='OK' THEN
						tWait(IN := FALSE,PT := T#10MS);
						GVL_Gepa.bSave := TRUE;
						nRequestStep := 4;
						ELSE
						nRequestStep := 10;
						nStep := 0;
					END_IF
				4:
					GVL_Gepa.bSave := TRUE;
					nRequestStep := 10;
					
			END_CASE
			END_WHILE
		IF nRequest <201 THEN
			nRequestStep := 0;
			ELSE
				nStep := 0;
		END_IF
		END_FOR
		//nStep := 0;
END_CASE

What i need is:
FOR nRequest = 0 do :
1. take sRequest := arrRequest [nRequest];
2. fbCheckSum(sSendString := sRequest);
3. SenString
4. ReceiveString and save string
5. wait 10ms
For nRequest = 1 do:
....
For nRequest = 200 do steps 1-5 and reset the sensor, then wait time is true, go from beginning nRequest = 0....
 
You need to repeat the case statement processing inside the outer case statement using a sub-step to implement the for/while loops: e.g. (not including your actual processing):



Code:
CASE #nStep OF
    0:
        #nStep := 1;
    1:
        #nStep := 2;
    2:
        #nStep := 3;
    3:
        #nStep := 4;
         #nSubStep := 0; //start for loop substep
        #nRequest := 0; //initialise for loop index
     4: //for loop
        CASE #nSubStep OF
            0:
               
                #nSubStep := 1;
            1:
                #nSubStep := 2;
            2:
                #nSubStep := 3;
            3:
                #nSubStep := 4;
            4:
                #nSubStep := 5;
            5:
                IF #nRequest < 200 THEN //continue with next element in for loop
                    #nRequest := #nRequest + 1;
                    #nSubStep := 0;
                ELSE //end of foor loop
                    #nStep := 0;
                END_IF;
        END_CASE;
END_CASE;
 
Last edited:
Code:
IF nRequest <201 THEN
	nRequestStep := 0;
ELSE
	nStep := 0;
END_IF
nRequest does not reach 201 while the loop is running, so nstep will never return to 0.
Try <200 maybe?
 
It is difficult to quickly comment on your code as 99% of your code is very specific on your sensor you want to use or your command set you want to use to address your sensor. But I see strange things in your code.

Do you know the difference between cyclic and acyclic commands/FBCalls?

Do you know that "waiting for 10MS" is never a good idea? Instead, wait for the ready flag to come on.
 
Hi L D[AR2,P#0.0], AustralIan and Noli2. Thank you for your feedback.

L D[AR2,P#0.0]:
I took your comment and implemented changes on nStep 3:
Code:
			nRequest := 0;	
	               nRequestStep := 0;
Unfortunately that is not solving my problem. On nStep no 4 I have a problem in inner case nRequestStep no 3 because SendString is not immediately ready, but nRequest continues growing anyway. So case inside of for loop is not done, but loop just continues.

AustralIan: nRequest reaches 201, only problem is that is reached before. Not in nRequestStep no 7 but when we are in nRequestStep no 3 because it stucks there.

Noli2: I am sorry. Under is simplified code, i hope is better. Actually i have some problems with understanding something..Looks like FOR loop is running no meter if instructions (case sentence under for loop) is done or not. How can i correct that? As written above, because Send String needs some time, nRequest just continues to rise...
And about "waiting for 10MS"...Sensor can not handle receiving new commands faster, that is why i put this condition. " Ready flag" could be that Send string is not busy anymore, but there it stucks also. Or did you have anything else in mind for " ready flag".

I think my problem here is for loop, case sentence with all the waiting and conditions works fine in this case.. i just dont know if i should add something or use something else instead FOR loop.


Code:
CASE nStep OF
	

	0: 
		//take reset command
		nStep := 1;
	1: 
		//Checksum is calculared
		nStep := 2;
	2: 
		//SendString is triggered and command with checksum and suffix is sent

		IF NOT fbSendString255.Busy THEN //SentString finished go on
			nStep := 3;
		END_IF
	3: 
		//Receive string is triggered
		//received command is checked: Ok or ERR
			
		IF sCheck ='OK' AND NOT fbReceiveString255.Busy (*AND some time conditions *) THEN 																																	
			nRequest := 0;	
	               nRequestStep := 0;
			nStep := 4;
		ELSIF sCheck = 'ER' THEN
			nStep := 0;
		END_IF
		
	4:

		FOR nRequest := 0 TO 200 DO
			CASE nRequestStep OF
				0: 
					sRequest := arrRequest[nRequest]; //Take Request command
					bSave := FALSE; //reset save value
					nRequestStep := 1;
				1: 
					//calculate checksum
					nRequestStep := 2;
				2:
					//trigger SendString
					nRequestStep := 3;
				3:
					IF NOT fbSendString255.Busy THEN //Wait SendString to finish
						nRequestStep := 4;
					END_IF
				4: 
					//Trigger request string
					sCheck := LEFT(sReceivedString,2); //check if Ok or error
					tWait(IN	:= TRUE,PT	:= T#10MS); //if not waiting is too fast for sensor
					nRequestStep := 5;
				5:
					IF tWait.Q AND NOT fbReceiveString255.Busy AND sCheck ='OK' THEN
						tWait(IN := FALSE,PT := T#10MS);
						nRequestStep := 6;
						ELSIF sCheck = 'ER' THEN
						nStep := 0;
					END_IF
				6:
					bSave := TRUE; //save values
					IF bSave THEN
					nRequestStep := 7;
					END_IF
				7: 
					IF nRequest <200 THEN
						nRequest := nRequest + 1;
						nRequestStep := 0;
					ELSIF nRequest = 201 THEN
						EXIT;
						nStep := 0;	
					END_IF			
				END_CASE
			END_FOR
 
If you continue along the for loop approach, your scan time will be a minimum of 200*10ms=2000ms and will be bigger depending on the time to receive the characters - if that is ok then continue with that method.


My posted code did not use a for loop instruction, but implemented the equivilant of a for loop using the substep case. If nRequest grows then you have an error in your code that changes the substep.
 
Last edited:
Oh yes, sorry it works that way. Thank you! I actually already tried that once but it didn't work, looks like i had also other bugs in that time.
I will also try without waiting for 10ms and see how will sensor behave.

Thank you!
 
... which means that i need to send 200 different command to the sensor in cycle. I tried to use FOR loop in two ways but unsuccessfully. I would appreciate if anybody has an Idea how to make it working.
...

You cant make it to work like that. Serial network is slow and you just can't make 200 different calls every cycle (well you "can", but then the cycle will be loooooooooooooooong).

We could give better advice if you tell us more about the application and the sensor you want to use and what you are trying to achieve.

hmm. somehow missed LD's post.
 

Similar Topics

Hello Everyone: I have been able to read value via canopen interface using ADS protocol. Actually my Structured Text Programming is weak. I can...
Replies
5
Views
3,974
Hello, CX9020 gets stuck after about two seconds after booting. Mouse pointer stops, won't allow access through system manager (timeouts) or web...
Replies
7
Views
5,449
Hi! I am trying to run the 'SimpleSample' (https://infosys.beckhoff.com/content/1033/TF5100_TC3_NC_I/Resources/3438746891/.zip ) to understand the...
Replies
2
Views
96
I am developing a library in twincat and one of the function uses IID_ITcVnAccess_TcVnPoint2_DINT and the definition of this type is defined in...
Replies
0
Views
67
Sorry if this has been asked before, and apologies if this seems like a trivial issue, but I am new to Beckhoff and have been banging my head...
Replies
2
Views
140
Back
Top Bottom