1

I cant get FB_MBReadinputs to work in Twincat, when Factory IO is sending/receiving Input and Holding registers.

First off here's my currently working snip of handling Modbus from Factory IO:

VAR
    GAB_FactoryIO_Inputs    AT %I*  :   ARRAY [0..15] OF BYTE;
... 
END_VAR

LFB_MBReadInputs(
    sIPAddr := '192.168.0.109',
    nTCPPort := 505,
    nUnitID := 255,
    nQuantity := 64,
    nMBAddr := 0,
    cbLength := SIZEOF(IO.GAB_FactoryIO_Inputs),
    pDestAddr := ADR(IO.GAB_FactoryIO_Inputs),
    bExecute := TRUE,
    tTimeout := T#1S,
    bBusy => ,
    bError => ,
    nErrId => ,
    cbRead => ,
 );
LFB_MBReadInputs(bExecute := FALSE);

It runs in state machine and churns out bytes happily, to be again written by MBWriteCoils.

But what I cant get to working is FB_MBReadRegs. Beckhoff examples look almost identical between MBReadInputs and MBReadRegs, I first got ADS error 1794 and changed VAR to DINT, and WORD, but now nothing happens instead of error. I'm expecting X, Y and Z coordinates to any bytes, bits or ints on registers, but absolutely nothing happens.

I've tried simplifying and figuring out where's the problem and here's whats going on currently:

VAR
GAB_FactoryIO_RegsIN    AT %I*  :   ARRAY [0..5] OF DINT;
    
LFB_MBReadRegs(
        sIPAddr := '192.168.0.109',
        nTCPPort := 505,
        nUnitID := 255,
        nQuantity := 16,
        nMBAddr := 0,
        cbLength := 100,
        pDestAddr := ADR(IO.GAB_FactoryIO_RegsIN),
        bExecute := TRUE,
        tTimeout := T#1S,
        bBusy => ,
        bError => ,
        nErrId => ,
        cbRead => ,
     );
    LFB_MBReadRegs(bExecute := FALSE);

Adding suggestions by @kolyur as far as I understand how it should go as part of state machine.

Apparently now that I had a new try at it, IF FB_MBReadRegs=bBusy THEN... doesnt work. "Error Functionblock 'FB_MBReadRegs' must be instantiated to be accessed". So thats a project for tomorrow to figure out.

After playing around for a while, I still dont fully grasp using or not using %I*, but %M* (or %MB0 and %MB64) did do the trick. Found part of the solution in https://forge.codesys.com/forge/talk/CODESYS-V2/thread/cc22cd1dc1/ Anyway, I misunderstood what MBReadRegs does, because MBReadInputRegs is where its at when receiving position info/whatever from Factorio.

Below apparently working code snip:

VAR_GLOBAL
    GAB_FactoryIO_Inputs    AT %I*  :   ARRAY [0..15] OF BYTE;  //I/O between Factory IO and TC
    GAB_FactoryIO_Outputs   AT %Q*  :   ARRAY [0..15] OF BYTE;
    GAB_FactoryIO_RegsIN    AT %MB0     :   ARRAY [0..5] OF WORD;  // %M* on both didnt work, coordinates spiraled to thousands
    GAB_FactoryIO_RegsOUT   AT %MB32    :   ARRAY [0..5] OF WORD;
    END_VAR


      
VAR;
    LFB_MBReadInputRegs     : FB_MBReadInputRegs  ;
END_VAR

    CASE iState OF
    //...other states in between
    10:  //next is in its own file

    LFB_MBReadInputRegs(
        sIPAddr := '192.168.0.109',
        nTCPPort := 505,
        nUnitID := 255,
        nQuantity := 12,
        nMBAddr := 0,
        cbLength := 100,
        pDestAddr := ADR(IO.GAB_FactoryIO_RegsIN),
        bExecute := TRUE,
        tTimeout := T#1S,
        bBusy => ,
        bError => ,
        nErrId => ,
        cbRead => ,
     );
    LFB_MBReadInputRegs(bExecute := FALSE);
    //5ms wait routine and jump to next iState

Another edit a bit later:

New situation where bErr 1794 just wouldnt go away.

  • For some reason cbLength didnt like 100 anymore, but had to have its size read
  • Also reboot. Useless amounts of struggling with troubleshooting would have been spared if I just cut the power from PLC power switch. Even though logically thinking software reboot is just as good, but memory problems got better with hard reset anyway.
IF NOT bModbusBusy4 THEN
        bModbusBusy4    := TRUE;
fbMBReadInputRegs (
    sIPAddr := ipAddr,
    nTCPPort := 506,
    nUnitID := 255,
    nQuantity := 32,
    nMBAddr := 0,
    cbLength := SIZEOF(TIO.GAB_FactoryIO_RegsIN),
    pDestAddr := ADR(TIO.GAB_FactoryIO_RegsIN),
    bExecute := TRUE,
    tTimeout := T#1S,
    bBusy => ,
    bError => ,
    nErrId => ,
    cbRead => ,
 );
  ELSE
    IF NOT fbMBReadInputRegs.bBusy THEN
        bModbusBusy4    := FALSE;
    END_IF
fbMBReadInputRegs(bExecute := FALSE);
END_IF

1 Answers1

1

Just a few things I noticed:

  • MBReadRegs will be writing to your array GAB_FactoryIO_RegsIN but you have this variable configured in the input space %I*. Is there a reason for that?
  • In your second example you are reading in 16 words but the destination variable is only 12 words (ARRAY [0..5] OF DINT). That could possibly be the cause of the ADS error 1794, "invalid index group". With MBReadRegs, nQuantity refers to 16-bit words whereas MBReadInputs counts bits.
  • You are triggering the bExecute bit on every scan. Generally you should trigger it once and wait for the bBusy bit to go false before triggering again.
kolyur
  • 93
  • 6
  • 1
    - I'm quite beginner and thought %I* would work when working with virtual inputs/outputs. Havent actually tried without it. - I'll have a try on changing it to 12 - Lets have a try with that, I'll edit the question with suggested edits as far as I know. Cheers mate – SpaceRodent Mar 29 '21 at 17:00
  • 2
    It generally isn't necessary to include an address with a variable declaration unless you have a specific reason. %I* and %Q* are necessary if you are linking the variable to physical I/O. %M is necessary in some cases, for example if you have an external comm driver accessing specific addresses over ADS. But otherwise you can just leave the address blank and TwinCAT will assign it automatically. – kolyur Mar 30 '21 at 12:44