# Modbus TCP in CODESYS



## Hubertus (7 September 2015)

Hallo,

ich möchte eine Kommunikation aufbauen. Auf der Slave Seite habe ich mehrere Soft SPS die in CODESYS erzeugt werden und auf einem Desktop PC laufen. Auf der Master Seite habe ich wiederum einen Desktop PC auf dem ein C++ Programm läuft. Diese sollen über ein LAN Kabel verbunden werden. Als Kommunikationsprotokoll soll Modbus TCP dienen.
Da ich in diesem Bereich neu bin ist mir die konkrete Umsetzung nicht klar. 

Auf der Homepage von CODESYS steht, dass Modbus TCP bereits voll integriert ist. Außerdem kann man über „Geräte hinzufügen“  zum Beispiel ein ModbusTCP_Slave_Divice hinzufügen. Hier komme ich leider nicht weiter. Welche Einstellungen muss ich vornehmen? Und vermutlich brauche ich noch eine Bibliothek mit den entsprechenden Befehlen um später Daten auszutauschen. Wo finde ich die, und müsste die nicht bereits schon enthalten sein, da ja Modbus TCP angeblich voll integriert ist. Ist dieser Ansatz mit Grät hinzufügen richtig? Oder geht das ganz anders? Wie ist das weitere Vorgehen?

Da ich mich momentan im Kreis drehe wäre ich über Hilfe sehr dankbar.
Gruß Hubertus


----------



## amos (7 September 2015)

Hallo Hubertus, nur so als Idee:

FUNCTION_BLOCK Ethernet_Server
VAR_INPUT
    xOPEN_SERVER            :    BOOL;            (* Set TRUE to enable function block            *)
    wPORT                    :    WORD;        (* Port number                                *)
    xTCP_PROTOCOL        :    BOOL;            (* TRUE => TCP; FALSE => UDP                *)
    tTIMEOUT                    :    TIME:=t#20s;    (* Watchdog timeout value (seconds)        *)
    iBYTES_TO_SEND        :    INT;            (* Number of bytes to transmit                *)
    ptSEND_BUFFER            :    POINTER TO ARRAY[1..1500] OF BYTE;    (* Transmit Buffer    *)
END_VAR
VAR_IN_OUT
    xSTART_SEND            :    BOOL;            (* Set TRUE to initiate data transmittal, will be    *)
                                                    (* reset FALSE when transmittal is completed     *)
    utRECEIVE_BUFFER        :    typEthernet_BUFFER;    (* Receive Buffer *)
END_VAR
VAR_OUTPUT
    utERROR                    :    ETH_ERROR;    (* See ETH_ERROR datatype in ETHERNET.LIB    *)
                                                    (* for error codes and definitions                        *)
    xSERVER_OPEN            :    BOOL;            (* TRUE if socket connection made                    *)
    wSOCKET                    :    WORD;        (* Socket connection number                            *)
END_VAR
VAR
    MyEthernetOpen    : ETHERNET_SERVER_OPEN;
    MyEthernetClose    : ETHERNET_SERVER_CLOSE;
    MyEthernetWrite    : ETHERNET_WRITE;
    MyEthernetRead    : ETHERNET_READ;
    EthernetBuffer        : ARRAY[1..1500] OF BYTE;
    Status                : WORD:=0;
    i                    : INT;
    getConfig            : ETHERNET_GET_NETWORK_CONFIG;
    watchdog_timer1    : TON;
    client_SRC_IP    WORD;
    client_SRC_PORT:WORD;
    CloseState        :INT;
END_VAR

IF xOpen_Server THEN
    watchdog_timer1(IN:=TRUE , PT:=tTIMEOUT );

    IF watchdog_timer1.q THEN
        watchdog_timer1(IN:=FALSE , PT:=tTIMEOUT );
        MyEthernetClose(EN:= 1, SOCKET:= MyEthernetOpen.SOCKET);
        MyEthernetClose(EN:= 0, SOCKET:= 0);
        Status := 0; (* Try to open the server *)
    END_IF

    CASE Status OF
        0:
            getConfig(EN:=FALSE );
            getConfig(EN:=TRUE );
            utERROR:=0;
            wSocket:=0;
            CloseState:=0;
            status:=1;
        1:    getConfig(EN:=TRUE );
            IF getConfig.ENO THEN
                IF getConfig.IP_ADR<>0 THEN
                    status:=4;
                    MyEthernetOpen (EN:=0);
                    MyEthernetRead(EN:= 0, SOCKET:=0 ,  DATA:=EthernetBuffer );
                    MyEthernetClose(EN:= 0);
                ELSE
                    status:=0;
                END_IF
            END_IF
        4:     (* try to open a socket*)
            IF xTCP_PROTOCOL THEN
                MyEthernetOpen(EN:=1 , TYP:=SOCK_STREAM, PROTO := IPPROTO_TCP, PORT := wPort);
            ELSE
                MyEthernetOpen(EN:=1 , TYP:=SOCK_DGRAM, PROTO := IPPROTO_UDP, PORT := wPort);
            END_IF
            IF ((MyEthernetOpen.ERROR = 0) AND (MyEthernetOpen.ENO = 1)) THEN
                Status  := 10; (* socket created, wait for data to process *)
                MyEthernetOpen (EN:=0);
                xServer_open:=TRUE;
                wSocket:=MyEthernetOpen.socket;
            ELSE
                Status := 0; (*socket not available, try it again *)
                MyEthernetOpen (EN:=0);
                xServer_open:=FALSE;
                wSocket:=0;
            END_IF;

        10:     (* Wait for data to process *)

            (* Monitor ethernet port for new data *)
             MyEthernetRead(    EN:= 1, SOCKET:=MyEthernetOpen.SOCKET ,  DATA:=EthernetBuffer );
            MyEthernetRead(    EN:= 0, SOCKET:=0 ,  DATA:=EthernetBuffer );

            IF(MyEthernetRead.ERROR<>0) THEN  (* Error reading the socket, close it *)
                Status:=40;
            ELSIF (MyEthernetRead.LEN_OUT <>0) THEN  (* Process the data received via the ethernet port *)
                Status:=20;
                client_SRC_IP:=MyEthernetRead.SRC_IP;
                client_SRC_PORT:=MyEthernetRead.SRC_PORT;
                watchdog_timer1(IN:=FALSE , PT:=tTIMEOUT );
                ELSIF (iBytes_TO_SEND > 0 AND xSTART_SEND) THEN  (* Process the data received via the serial port *)
                status:=30;

                MyEthernetWrite(    EN:= 0 , DATA:=EthernetBuffer );
            END_IF

        20:    (* Process the data received via the ethernet port *)

            FOR i := 1 TO (MyEthernetRead.LEN_OUT) DO
                utReceive_buffer.index :=(utReceive_buffer.index MOD 1500)+1;
                utReceive_buffer.data[utReceive_buffer.index] := EthernetBuffer_;
            END_FOR

            Status:=10;

        30:     (* Process the data to send *)

            EthernetBuffer :=ptSEND_BUFFER^;

                (* Send data in transmit buffer out ethernet port *)
                IF xTCP_PROTOCOL THEN
                        MyEthernetWrite(    EN:= 1,
                                    SOCKET:= MyEthernetOpen.SOCKET,
                                    LEN_IN:=iBYTES_TO_SEND,
                                    DATA:=EthernetBuffer );
                ELSE
                    IF client_SRC_IP>0 AND client_SRC_PORT>0 THEN
                        MyEthernetWrite(EN:=1, SOCKET:=MyEthernetOpen.SOCKET , LEN_IN:=iBYTES_TO_SENd , DST_IP:=client_SRC_IP,DST_PORT:=client_SRC_PORT,DATA:=EthernetBuffer );
                    ELSE
                        status:=10;
                    END_IF
                END_IF

                IF MyEthernetWrite.eno THEN
                    IF(MyEthernetWrite.ERROR <>0) THEN
                        Status:=40;(* Error writing to the socket, close it *)        
                    ELSE
                        Status:=10; (* Comms complete, read more data *)
                    END_IF;
                    xStart_send:=FALSE;
                END_IF


        40:    (* Close the server *)

            MyEthernetClose(EN:= 1, SOCKET:= MyEthernetOpen.SOCKET);
            IF MyEthernetClose.eno THEN
                MyEthernetClose(EN:= 0);
                xServer_open:=FALSE;
                utERROR:=0;
                wSocket:=0;
                Status := 0; (* Try to open the server again *)
            END_IF

    END_CASE;
ELSE
    CASE CloseState OF
    0:
        MyEthernetClose(EN:= 0);
        MyEthernetClose(EN:= 1, SOCKET:= MyEthernetOpen.SOCKET);
        CloseState:=1;
    1:
        MyEthernetClose(EN:= 1, SOCKET:= MyEthernetOpen.SOCKET);
        IF MyEthernetClose.eno THEN
            MyEthernetClose(EN:= 0);
            CloseState:=2;
        END_IF
    2:
        xServer_open:=FALSE;
        utERROR:=0;
        wSocket:=0;
    END_CASE
END_IF_


----------



## amos (7 September 2015)

Sieh Dir am besten mal die Dokumentation der network lib an:
http://www.oscat.de/dlmanager.html


----------



## amos (7 September 2015)

Hallo Hubertus,
wenn es statt C++ auch Perl sein darf kann man das auch so machen:

#!/usr/bin/perl
use strict;
use Modbus::Client;
use FileHandle;
use Socket;
 my $fd = new FileHandle;
 my $host = "192.168.178.3";
 my $port = 502;
 my $remote = inet_aton($host)  || die "No such host $host";
 socket($fd, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
                                || die "Can't create socket - $!";
 my $paddr = sockaddr_in($port, $remote);
 connect($fd, $paddr)        || die "Can't connect to $host:$port - $!";
 $fd->autoflush(1);
 my $bus = Modbus::Client->new($fd);
 my $unit = $bus->device(1);
 my  @array = $unit->read(30001..30010);
 print "a1: @array\n";
 my $hash  = $unit->read(10001..10037);
 my %hash = map {$_, 1} (20007, 20012..20015, 30029, 40006);
 @array = $unit->read(\%hash);
 $hash  = $unit->read(\%hash);
 print "a2: @array\n";
 # $unit->write(6 => 1, 8 => 0, 2007 => 10, 2008 => 2);
 #%hash = (6 => 1, 8 => 0, 2007 => 10, 2008 => 2);
 #$unit->write(%hash);
 #$unit->write(\%hash);


----------



## amos (7 September 2015)

Hier noch eine sehr gute Doku für einen Testaufbau mit einem iPod statt PC und der passenden fertigen App dazu:
http://www.sweetwilliamsl.com/wp-content/uploads/2013/04/Anwendungshinweis_iPod.pdf


----------



## amos (7 September 2015)

und damit der Thread auch wirklich vollständig ist, hier noch der c++ quellcode einer vollständigen Modbus Master Applikation:
http://sourceforge.net/projects/qmodmaster/files/qModMaster-Source-0.4.4.zip


----------

