I need to interview two Modbus RTU devices (drink + reading).
I ran into a problem that there is no example with a record and reading.
I use the library WagoApplCmodbus.
Now I have a decision with four FB. Can you do through one FB?
I can write the code below, if necessary.
No, unfortunately with RTU you will need to cycle through the four slaves in a state machine in one Function Block.
The way I have done it is with a case statement, and reconfigure the function block between messages. Something like this…
CASE state OF
0: // Reconfigure Modbus master
fBModbusMaster.id := 3; // Update the Modbus ID
fBModbusMaster.xTrigger := TRUE; // Re-enable the function block
state := 1;
1: IF NOT fBModbusMaster.xTrigger THEN
state := 2;
END_IF
2: fBModbusMaster.id := 4; // Update the Modbus ID
fBModbusMaster.xTrigger := TRUE; // Re-enable the function block
state := 3;
3: IF NOT fBModbusMaster.xTrigger THEN
state := 0;
END_IF
END_CASE;
That’s about what I thought. Can we use one FB modbus or do we need each one for its own function (read/write) ?
Is this how it’s gonna work? Where do I change the utQuery parameters? If I understand correctly only this parameter is responsible for the address and function when polling.
PROGRAM prgMB_Master
VAR
fbMasterSerial_MB : WagoAppPlcModbus.FbMbMasterSerial;
xConnect : BOOL :=FALSE;
xIsConnected : BOOL;
xError : BOOL;
oStatus : WagoAppPlcModbus.WagoSysErrorBase.FbResult;
utQuery : typMbQuery := (bUnitId := 2, bFunctionCode := 16#10, uiReadAddress := 0, uiReadQuantity := 0, uiWriteAddress := 512, uiWriteQuantity := 7);
xTrigger : BOOL;
utResponse : typMbResponse;
tTimeOut : TIME := T#300MS;
tQueryTime : TIME := T#200MS;
utQuery_1 : typMbQuery := (bUnitId := 2, bFunctionCode := 16#03, uiReadAddress := 525, uiReadQuantity := 8, uiWriteAddress := 0, uiWriteQuantity := 0);
utQuery_2 : typMbQuery := (bUnitId := 3, bFunctionCode := 16#10, uiReadAddress := 0, uiReadQuantity := 0, uiWriteAddress := 512, uiWriteQuantity := 7);
utQuery_3 : typMbQuery := (bUnitId := 3, bFunctionCode := 16#03, uiReadAddress := 525, uiReadQuantity := 8, uiWriteAddress := 0, uiWriteQuantity := 0);
END_VAR
fbMasterSerial_MB(
I_Port := IoConfig_Globals.COM1,
udiBaudrate := 19200 ,
usiDataBits := 8,
eParity := eTTYParity.None,
eStopBits := eTTYStopBits.One ,
eHandshake := eTTYHandshake.None ,
ePhysical := eTTYPhysicalLayer.RS485_HalfDuplex,
//xIsConnected=> xIsConnected ,
//xError => xError,
//oStatus => oStatus ,
eFrameType := eMbFrameType.RTU ,
tTimeOut := tTimeOut ,
//utQuery := utQuery,
//xTrigger := xTrigger ,
//utResponse := utResponse );
CASE state OF
0:
fbMasterSerial_MB(
xConnect := FALSE ,
utQuery := utQuery,
xTrigger := xTrigger ,
utResponse := utResponse );
fbMasterSerial_MB.xIsConnected;
fbMasterSerial_MB.xError;
fbMasterSerial_MB.oStatus;
state := 1;
1:
fbMasterSerial_MB(
xConnect := TRUE ,
utQuery := utQuery,
xTrigger := xTrigger ,
utResponse := utResponse );
IF fbMasterSerial_MB.xIsConnected = TRUE THEN
xTrigger := TRUE;
state := 2;
END_IF
IF fbMasterSerial_MB.xError = TRUE THEN
state := 0;
END_IF
2:
utQuery.awWriteData[0] := BOOL_TO_WORD( Modbus[1]);
utQuery.awWriteData[1] := BOOL_TO_WORD( Modbus[2]);
fbMasterSerial_MB(
xConnect := TRUE ,
utQuery := utQuery,
xTrigger := xTrigger ,
utResponse := utResponse );
IF fbMasterSerial_MB.oStatus.sDescription = 'OK' THEN
state := 3;
END_IF
IF fbMasterSerial_MB.oStatus.sDescription = 'Error time out' THEN
state := 0;
END_IF
3:
fbMasterSerial_MB(
xConnect := FALSE ,
utQuery := utQuery,
xTrigger := xTrigger ,
utResponse := utResponse );
state := 4;
4:
fbMasterSerial_MB(
xConnect := TRUE ,
utQuery := utQuery_1,
xTrigger := xTrigger ,
utResponse := utResponse );
IF fbMasterSerial_MB.xIsConnected = TRUE THEN
xTrigger := TRUE;
state := 5;
END_IF
IF fbMasterSerial_MB.xError = TRUE THEN
state := 0;
END_IF
5:
NEXT.....
I had the same issue. Digital Twins worked the best for me:
FUNCTION_BLOCK FbSpecialSlave EXTENDS FbDigitalTwinMbSlaveDevice
VAR_INPUT
bUnitId : BYTE := 1;
tScanRate : TIME := T#200MS;
END_VAR
VAR_OUTPUT
xError : BOOL;
sInfo : STRING;
wValue1 : WORD;
wValue2 : WORD;
wValue3 : WORD;
END_VAR
VAR
xTrigger : BOOL;
m_FbQuery_0 : WagoAppPLCModbus.FbQuery(THIS^);
m_FbQuery_1 : WagoAppPLCModbus.FbQuery(THIS^);
m_FbQuery_2 : WagoAppPLCModbus.FbQuery(THIS^);
wState : WORD;
oDelay : TON;
END_VAR
CASE wState OF
0:
IF IMbMasterMultiQuery = 0 THEN
RETURN;
ELSIF NOT IMbMasterMultiQuery.xConnected THEN
RETURN;
ELSE
wState := 10;
END_IF
10:
oDelay(
IN := TRUE,
PT := tScanRate );
IF oDelay.Q THEN
oDelay(
IN := FALSE );
m_FbQuery_0.bUnitId := bUnitId;
m_FbQuery_0.bFunctionCode := 3;
m_FbQuery_0.uiReadAddress := 0;
m_FbQuery_0.uiReadQuantity := 1;
protAttachMbQuery(m_FbQuery_0);
m_FbQuery_1.bUnitId := bUnitId;
m_FbQuery_1.bFunctionCode := 3;
m_FbQuery_1.uiReadAddress := 1;
m_FbQuery_1.uiReadQuantity := 1;
protAttachMbQuery(m_FbQuery_1);
m_FbQuery_2.bUnitId := bUnitId;
m_FbQuery_2.bFunctionCode := 3;
m_FbQuery_2.uiReadAddress := 2;
m_FbQuery_2.uiReadQuantity := 1;
protAttachMbQuery(m_FbQuery_2);
END_IF
END_CASE
This FB has two methods
METH: onError
(*
after each request with errors is method called
*)
METHOD onError : bool
VAR_INPUT
IQuery : I_Query;
utResponse : typMbResponse;
oStatus : WagoSysErrorBase.FbResult;
END_VAR
xError := TRUE;
sInfo := oStatus.GetDescription();
METH onResponse:
METHOD onResponse : BOOL
VAR_INPUT
IQuery : I_Query;
utResponse : typMbResponse;
END_VAR
xError := FALSE;
sInfo := 'OK';
IF IQuery = m_FbQuery_0.IQuery THEN
wValue1 := utResponse.awData[0];
ELSIF
IQuery = m_FbQuery_1.IQuery THEN
wValue2 := utResponse.awData[0];
ELSIF
IQuery = m_FbQuery_2.IQuery THEN
wValue3 := utResponse.awData[0];
END_IF
No use the FbMbMasterMultiQuerySerial as your Master. connect the IMbMasterMultiQuery from your master to the Slave Devices. I_Port ist the 750-652 Card.
I hope this helps.
Cheers