We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you've provided to them or that they've collected from your use of their services.

RS232 communication with Satel Integra 64

Until a few days ago the communication between my PLC and the Satel Integra 64 alarm installation had been done through a relay outputs of Integra (CA-64 O-R), which closed circuits connected to digital inputs (DI) of the PLC.  All had worked well but due to a limitation in the number of wires available between the alarm and the PLC, any further extension of the sensor/event number was impossible.

A week ago I have installed an alternative solution based on RS232.  On the hardware side a WAGO 750-650/003-000 module was needed and also Satel INT-RS. I have additionally downloaded Serial_Interface_01.lib library (available at Wago site) and programming instruction published by Satel. I have also received na example of a program from Wago. For the help in making it all work - great thanks to the Wago guys!

As to configuration: on the side of INT-RS – functioning mode controled by microswitches (integration with other systems): switch 5 ON. On the side of 750-650/003 – Baudrate: 19200, Data frame: 8 databits, Stopbits: 1, Data bytes: 5, RTS/CTS: Disable…

Please be warned that the presented code is a work of an amatour and can be against any programming principles. It has, however, been tested and works so far fine:

In the variable definitions:

VAR
	(*interface definitions*)
	CommContext : SERIAL_INTERFACE;
	Init : BOOL;
	Error : BYTE;
	IsOpen : BOOL; 
	Send : BOOL:=TRUE; (*send launch, reset on send end*)
	RMessage : typRING_BUFFER; (*received message*)
	Message : POINTER TO BYTE;
	MessageLEN : BYTE;

	(*default message to be sent*)
	Message7F : ARRAY [0..6] OF BYTE:=254, 254, 127, 216, 97, 254, 13;

	(*variables supervising communication flow*)
	SendEnds : F_TRIG;
	SendStarts : R_TRIG;
	WaitForData : BOOL;
	StopWaiting : TON;
	FrameComplete : BOOL;
	LastRecord : INT; (*stores last reviewed byte*)
	SyncSignal : BOOL:=FALSE;
	EndSignal : BOOL:=FALSE;

	(*variables for RMessage analysis*)
	mStart, mEnd : INT;
	crcHigh, crcLow : BYTE;
	RMessageCRC : CRC_Calculate;
	Command : BYTE;

	(*variables for preparing message on demand*)
	FoundNewState : BOOL;
	MessageState : typRing_BUFFER;
	MessageStCrc : CRC_Calculate;
	CrcLenExtension : BYTE;

	(*variables storing data received from Integra*)
	Sensors : ARRAY[0..55] OF BOOL;
	NewStates : ARRAY[0..39] OF BOOL;
	
	i : BYTE;
END_VAR

The program continuously sends to the alarm system the command 0x7F meaning „list of new states”. The answers are place in the NewStates array. At the beginning of each cycle, the array is analyzed.  If any of the states = TRUE, the next commands, which are sent, ask for those states. After all of them are red (asked for), the program returns to sending 0x7F command. Here is the code:

SendStarts(CLK:=Send); (*trigger sensing the begin of sending*)

IF SendStarts.Q THEN (*if sending starts*)
	(*prepare the variables*)
	FoundNewState:=FALSE;
	i:=0;

	(*checking if there is an unread Integra64 state stored in NewStates ARRAY*)
	WHILE (i<40) AND (NOT FoundNewState) DO
		IF NewStates[i] THEN (*new event found*)
			FoundNewState:=TRUE; (*marker to finish searching*)
			NewStates[i]:=FALSE; (*reset the state in NewState ARRAY*)
			CrcLenExtension:=0;


			(*preparing new message to be sent*)
			MessageState.Data[0]:=254;
			MessageState.Data[1]:=254;
			MessageState.Data[2]:=i; (*command number=index in the ARRAY*)
			MessageStCrc(m:=ADR(MessageState), start:=2, end:=2); (*calculate CRC*)
			MessageState.Data[3]:=WORD_TO_BYTE(MessageStCrc.hi);

			IF MessageStCrc.hi=254 THEN (*if CRC.hi=254 add 240 after it*)
				MessageState.Data[4]:=240;
				CrcLenExtension:=1;
			END_IF;

			MessageState.Data[4+CrcLenExtension]:=WORD_TO_BYTE(MessageStCrc.lo);

			IF MessageStCrc.lo=254 THEN (*if CRC.lo=254 add 240 after it*)
				MessageState.Data[5+CrcLenExtension]:=240;
				CrcLenExtension:=CrcLenExtension+1;
			END_IF;

			MessageState.Data[5+CrcLenExtension]:=254;
			MessageState.Data[6+CrcLenExtension]:=13;
			MessageState.Index:=7+CrcLenExtension;
		END_IF; (*End of IF new event found*)

		i:=i+1;
	END_WHILE; (*end of loop for checking NewState ARRAY*)

	(*the clue of this IF statement – decide what message to send*)
	IF FoundNewState THEN (*if there is an new state to be red*)
		Message:=ADR(MessageState.Data);
		MessageLen:=INT_TO_BYTE(MessageState.Index);
	ELSE (*otherwise send the standard 7F message*)
		Message:=ADR(Message7F);
		MessageLEN:=7;
	END_IF;
END_IF; (* End of IF Send Start detected*)

(*definition of the Communication module*)
CommContext(
	xOPEN_COM_PORT:=TRUE,
	bCOM_PORT_NR:=2,
	cbBAUDRATE:=1920,
	cpPARITY:=0,
	csSTOPBITS:=1,
	cbsBYTESIZE:=8,
	cfFLOW_CONTROL:= 0,
	iBYTES_TO_SEND:=MessageLEN,
	ptSEND_BUFFER:=Message, (*here is where the message pointer goes*)
	xSTART_SEND:=Send,
	utRECEIVE_BUFFER:=RMessage,
	xINIT:= Init,
	bERROR=>Error,
	xCOM_PORT_IS_OPEN=> IsOpen);

SendEnds(CLK:= Send); (*trigger sensing the end of sending*)

IF SendEnds.Q THEN (*if sending ends…*)
	WaitForData:=TRUE;
END_IF;

(* Impulse used to start all over if correct answer is not coming*)
StopWaiting(IN:= WaitForData, PT:=t#1s);

IF WaitForData THEN
	(*looking for the bytes 0xFE 0xFE = frame starts
	repeated in next cycles despite finding one 0xFE 0xFE
	in case new sync frame signal comes
	analysis starts from where it ended in the previous cycle*)

	WHILE (LastRecord<RMessage.Index-1) DO 
		IF RMessage.Data[LastRecord]=254 AND RMessage.Data[LastRecord+1]=254 THEN
			SyncSignal:=TRUE;
			mStart:=LastRecord+2; (*message start marker*)
			LastRecord:=LastRecord+1;
		END_IF;
		LastRecord:=LastRecord+1;
	END_WHILE;

	(*reset LastRecord to where the 0xFE 0xFE ended*)
	IF SyncSignal THEN
		LastRecord:=mStart;
	END_IF;

	(*looking for bytes 0xFE 0x0D = frame ends*)
	WHILE (LastRecord<RMessage.Index-1) AND SyncSignal DO 
		IF RMessage.Data[LastRecord]=254 AND RMessage.Data[LastRecord+1]=13 THEN
			EndSignal:=TRUE;
			mEnd:=LastRecord-1; (*message end marker*)
		END_IF;
		LastRecord:=LastRecord+1;
	END_WHILE;

	(*if a complete frame has been received*)
	IF SyncSignal AND EndSignal THEN
		(*check CRC, watch out for 240 bytes - they should be dropped*)
		IF RMessage.Data[mEnd]=240 THEN (*if one of the CRC numbers is 0xF0, drop it*)
			mEnd:=mEnd-1;
		END_IF;
		crcLow:=RMessage.Data[mEnd];

		IF RMessage.Data[mEnd-1]=240 THEN (*if one of the CRC numbers is 0xF0, drop it*)
			mEnd:=mEnd-1;
		END_IF;
		crcHigh:=RMessage.Data[mEnd-1];
		mEnd:=mEnd-2;

		(*function block for calculating CRC*)
		RMessageCRC(m:=ADR(RMessage), start:=mStart, end:=mEnd);

		(*check if CRC is okay*)
		IF RMessageCRC.hi=crcHigh AND RMessageCRC.lo=crcLow THEN
			(*analysis of the received command*)
			Command:=RMessage.Data[mStart];
			CASE Command OF
				(*zones violation*)
				0: IF (mEnd-mStart)>15 THEN (*check for required response length*)
					FOR i:=0 TO 6 DO
						Sensors[0+i*8]:=RMessage.Data[mStart+1+i].0;
						Sensors[1+i*8]:=RMessage.Data[mStart+1+i].1;
						Sensors[2+i*8]:=RMessage.Data[mStart+1+i].2;
						Sensors[3+i*8]:=RMessage.Data[mStart+1+i].3;
						Sensors[4+i*8]:=RMessage.Data[mStart+1+i].4;
						Sensors[5+i*8]:=RMessage.Data[mStart+1+i].5;
						Sensors[6+i*8]:=RMessage.Data[mStart+1+i].6;
						Sensors[7+i*8]:=RMessage.Data[mStart+1+i].7;
					END_FOR;
				END_IF;
				
				(*here other commands are to be filled*)

				(*list new states*)
				127: IF (mEnd-mStart)>4 THEN (*check for required response length*)
					FOR i:=0 TO 4 DO
						NewStates[0+i*8]:=RMessage.Data[mStart+1+i].0;
						NewStates[1+i*8]:=RMessage.Data[mStart+1+i].1;
						NewStates[2+i*8]:=RMessage.Data[mStart+1+i].2;
						NewStates[3+i*8]:=RMessage.Data[mStart+1+i].3;
						NewStates[4+i*8]:=RMessage.Data[mStart+1+i].4;
						NewStates[5+i*8]:=RMessage.Data[mStart+1+i].5;
						NewStates[6+i*8]:=RMessage.Data[mStart+1+i].6;
						NewStates[7+i*8]:=RMessage.Data[mStart+1+i].7;
					END_FOR;
				END_IF;
			END_CASE;
		END_IF; (*End of IF CRC OK*)

		(*marker to finish waiting and send new message*)
		FrameComplete:=TRUE;
		
	END_IF; (*END of IF a frame has been received*)
END_IF; (*END of IF WaitingForData*)

(*if a complete frame has been received or waiting time has passed*)
IF StopWaiting.Q OR FrameComplete THEN
	(*reset all the variables*)
	WaitForData:=FALSE;
	RMessage.Index:=0;
	SyncSignal:=FALSE;
	EndSignal:=FALSE;
	LastRecord:=0;
	mStart:=0;
	mEnd:=0;
	FrameComplete:=FALSE;
	Send:=TRUE;
END_IF;

The program uses the CRC_Calculate function block, which calculates CRC in line with the principles set in Satel's INT-RS documentation. The code:

FUNCTION_BLOCK CRC_Calculate

VAR_INPUT
	m : POINTER TO typRing_BUFFER;
	start : INT;
	end :INT;
END_VAR

VAR_OUTPUT
	lo : WORD;
	hi : WORD;
	crc : WORD;
END_VAR

VAR
	i : INT;
	d : WORD;
	tcrc : WORD;
END_VAR

* * * * *

IF end=0 THEN
	end:=m^.Index;
END_IF;

tcrc:=16#147A;

FOR i:=start TO end DO
	tcrc:=ROL(tcrc, 1);
	tcrc:=tcrc XOR 16#FFFF;
	tcrc:=tcrc+HEX_HI(in:=tcrc)+m^.Data[i];
END_FOR;

crc:=tcrc;
d:=tcrc / 4096;

hi:=d*16;
tcrc:=tcrc-d*4096;
d:=tcrc/256;
hi:=hi+d;

tcrc:=tcrc-d*256;
d:=tcrc/16;
lo:=d*16;
tcrc:=tcrc-d*16;
lo:=lo+tcrc;

...the function block above uses a HEX_HI function:

FUNCTION HEX_HI : WORD

VAR_INPUT
	in : WORD;
END_VAR

VAR
	div4096 : WORD;
END_VAR

* * * * *

div4096:=in/4096;
HEX_HI:=div4096*16;
in:=in-4096*div4096;
HEX_HI:=HEX_HI+in/256;

Finally the whole set comprises of 3 elements: RS232 program, CRC_Calculate function block and HEX_HI function:

RS232

..what else is to be done? The states of the movement sensors (placed in the Sensors array), they can be followed from the main program PLC_PRG:

VAR
	Move_Wejscie,
	Move_Hol,
	(...)
	Move_Kotl : R_TRIG;
END_VAR{/codecitation}

* * * * *

Move_Wejscie(CLK:=RS232.Sensors[0]);
Move_Hol(CLK:=RS232.Sensors[1]);
(...)
Move_Kotl(CLK:=RS232.Sensors[12]);

When a movement is discovered by any of the sesors, Integra, while replying to the 127 command will inform about the new state available under command 0 (NewStates[0]=TRUE).  The next command sent by the PLC is 0; Integra will reply informing, which sensor has been ignited (Sensors[x]=True).  That event will be recorded by one of the triggers from the main program PLC_PRG, which for 1 cycle will return Q=TRUE (Move_xxx.Q=TRUE).  This fact can be used to, for example, turn on the lights in a corridor (Light_XXX is a Fb_LatchingRelay funcion block):

LIGHT_XXX(xSwitch:=IN_X, xCentON:=Move_xxx.Q);

I cannot list any business/money-wise arguments to support abandoning the previous solution (communication via relays and DI).  Moving to RS232 was driven more by curiosity.  Additionally I was slightly annoyed by the clicking sound I could hear despite placing the relays in the metal box of the alarm installation and closed door of that room... Exploring the unknown, trying and testing, has been, as usual, a source of many priceless 'wow' effects.