1. Overview

1.1. Introduction

SoftPLC® is a registered trademark of SoftPLC Corporation located in Spicewood, Texas United States. The SoftPLC runtime is an embedded software program developed by SoftPLC Corporation.

Ethernet/IP™ is a trademark of ODVA, Inc.

This document describes the installation, usage, and functionality of the Ethernet/IP SoftPLC add-on module for the SoftPLC runtime. This software is a TOPDOC Loadable Module (TLM) that extends a SoftPLC runtime engine, enabling it to be a both a master/scanner and slave/adapter on one Ethernet/IP network. One ethernet interface is supported.

1.2. Concepts

The SoftPLC runtime engine software supports TLM’s, which are shared library extensions to SoftPLC. A TLM may be loaded either as a DRIVER or as a MODULE. The difference between a DRIVER and a MODULE is that a DRIVER is called once per SoftPLC scan cycle, and optionally an additional number of times per scan. A MODULE is only called when the control program decides to call it and not as an inherent part of the scan. TLM’s are made known to SoftPLC in the MODULES.LST file which may be edited by TOPDOC NexGen by traversing to: PLC ▸ Modules.

This ETHER_IP TLM is a DRIVER and has a number of TOPDOC Loadable Instructions contained within it. These are Ladder Logic instructions that can be used to control or query the operation of the TLM.

1.3. Features

  • Five milli-second timing granularity.

  • I/O scanner: up to 128 total originating i/o connections to as many target slaves as needed.

    • each i/o connection may have its own forward open timeout, RPI timeout multiplier, connection type, connection trigger type,

    • each i/o connection half may have its own RPI, size, target and originating assembly, and realtime format.

  • Up to 20 target i/o connections in the form of exclusive owner.

  • Up to 3 input only i/o connections.

  • Up to 2 listen only i/o connections.

  • I/O connections may be unicast or multicast.

  • Explicit messaging server supporing:

    • assembly class 0x04 instances,

    • symbol class 0x6b instances, per Rockwell Automation publication 756-PM020D-EN-P - June 2016. This lets a SoftPLC runtime look like a Rockwell PLC to various HMIs. A descriptor tag may be exported as a CIP symbol and it is tied to a unique assembly instance when exported.

  • Up to 1000 words per assembly.

1.4. Limitations

  • No explicit messaging client at this time, only server support as stated in Features above.

  • The total number of I/O connection bytes is limited by the size of the SoftPLC runtime license, contact sales for details.

1.5. Requirements

  • SoftPLC version 4.6 or greater

  • TOPDOC NexGen version 1.6 or greater

2. Warranty

2.1. Terms of Use

Because of the variety of uses of the information described in this manual, the users of, and those responsible for applying this information must satisfy themselves as to the acceptability of each application and use of the information. In no event will SoftPLC Corporation be responsible or liable for its use, nor for any infringements of patents or other rights of third parties which may result from its use.

SOFTPLC CORPORATION MAKES NO REPRESENTATIONS OR WARRANTIES WITH RESPECT TO THE CONTENTS HEREOF AND SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE.

SoftPLC Corporation reserves the right to change product specifications at any time without notice. No part of this document may be reproduced by any means, nor translated, nor transmitted to any magnetic medium without the written consent of SoftPLC Corporation.

SoftPLC, and TOPDOC are registered trademarks of SoftPLC Corporation.

© Copyright 2018 SoftPLC Corporation, ALL RIGHTS RESERVED

First Printing

October, 2018

Latest Printing

October, 2018

SoftPLC Corporation
25603 Red Brangus Drive
Spicewood, Texas 78669

USA Telephone: 1-800-SoftPLC
WW Telephone: 512/264-8390
Fax: 512/264-8399
URL: http://softplc.com
Email: support@softplc.com

3. Software Installation

The TLM is in a file named ether_ip.tlm.so and the configuration file is named ETHER_IP.LST. These files will be pre-installed on your new SoftPLC for you. If you have an older SoftPLC, you can use SFTP to transfer the 2 above files to the /SoftPLC/tlm directory on the SoftPLC.

To enable the TLM within the SoftPLC, use TOPDOC NexGen. Traverse to PLC ▸ Modules and select Use for ETHER_IP.TLM. There are no command line Options for this TLM (which would go into the fourth column if there were any).

PLC Panel

3.1. Editor Usage

The Load button will load the MODULE.LST file from the development system’s disk.
The Save button will write the MODULE.LST file to the development system’s disk.
The Fetch button will load the MODULE.LST file from the runtime system’s disk.
The Send button will write the MODULE.LST file to the runtime system’s disk.

It is good practice to both Save and Send the edits, this way both your development system and the runtime get a copy.
Whenever you Send the list of modules or the configuration file, you must restart or cycle power on the SoftPLC in order for the changes to take effect.
To add your TLM to TOPDOC NexGen to enable it for use with offline editing, copy the ether_ip.tlm.so file into your <dev-sys-install-base>/SoftPLC/tlm/ directory (where <dev-sys-install-base> is where you have installed TOPDOC NexGen).

The next step is to Configure the TLM.

4. Configuration

The detailed driver setup is done through the configuration file ETHER_IP.LST. Click the Configure button below the list of modules to edit the ETHER_IP.LST file (note that editing the configuration file in this manner requires a connection to the SoftPLC if you use either the Fetch or Send options).

The default file is shown below - the comments in the file explain the format.

4.1. Configuration Editor Usage

The configuration file for the ETHER_IP TLM is ETHER_IP.LST.

The Load button will load the ETHER_IP.LST file from the development system’s disk.
The Save button will write the ETHER_IP.LST file to the development system’s disk.
The Fetch button will load the ETHER_IP.LST file from the runtime system’s disk.
The Send button will write the ETHER_IP.LST file to the runtime system’s disk.

After you Send the list of modules or the configuration file, you must restart or cycle power on the SoftPLC in order for the changes to take effect.

4.2. ETHER_IP.LST Configuration File Details

The configuration file is used to set the debug level, specify which ethernet interface, define local assemblies, declare expectations for target (incoming) i/o connections, and define originator (outbound) i/o connections.

The comments in the sample file below explain the format.

Sample ETHER_IP.LST
# Ethernet/IP Driver Configuration for SoftPLC Runtime Software.
# This file uses an "s-expression" format, which is like XML but uses
# parentheses instead of angle bracketed keywords.  The syntax of s-expression
# files is well documented and can be googled.  The grammar, on the other hand,
# unlike the syntax, is application specific.  SoftPLC uses '#' to indicate
# a single line comment. If the first non-blank character is #, then this
# line is a single line comment.  Single line comments cannot follow
# any other token on the same line.  Multiline 'C' style comments are also
# supported.  Its best to use these also only as the first non-blank combo:
# /* followed by an eventual */
#
# S-expressions are thought to be more human readable than XML
# and are easy to parse with a programming language.  Parentheses are balanced.
#
# The supported grammar is documented in the next several lines of comments.
# Grammar rules are declared like this, for example:
# (timeout_mult <4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 > )
# This means "timeout_mul" is a keyword and it can be followed with
#   4, 8, 16, 32, 64, 128, 256, or 512.  The '|' means "or". e.g. (timeout_mult 4)
#
# If something is wrapped in square brackets [] in a grammar rule, this means
# whatever appears in the brackets is optional.  If omitted, then a default value
# will be used and the default is defined in the grammar rule by a comment following
# a ':'.  The square brackets, being only part of the grammar definition,
# are never actually used in real configuration statements.
#
# If something is wrapped in angle brackets <> in a grammar rule, this means
# it is a grouping.  Only one of the choices in a grouping may actually be used.
# Angle brackets, being only part of the grammar definition, are never actually
# used in real configuration statements.
#
# White spaces includeing tabs and newlines (CR, LF, etc.) are all ignored.
# Numbers may be provided either in decimal or in hex with a prefix of 0x.

#------ Grammar Rules: ----------
# (ethernet_ip                  ` is the master containing element and it may contain
#                               ` the indicated 6 top level elements.  Some of the
#                               ` 6 are optional, indicated with enclosing [].
#                               ` Every nested element is contained within this
#                               ` master element.
#   (driver_version ...)
#   (interface ...)
#   (debug ...)
#   [(my_assemblies ...)]
#   [(target_role_connections ...)]
#   [(originator_role_connections ...)]
#   )  /* end of "ethernet_ip" s-expression element */
#
# Details on nested elements:
#
# (driver_version <VERSION_NUM>)            ` VERSION_NUM should be 1
# (interface <ETH_PORT>)                    ` which ethernet interface to use, e.g. eth0
# (debug <BIT_SET>)                         ` turns on debugging features given in
#                                           ` the integer bitset.
#
# Section (my_assemblies ...) causes the creation of the listed assemblies in
# the CIP domain and ties them into the SoftPLC datatable based on what PLC
# address the "tag" is defined to in the descriptor table.  Assemblies each
# must have a unique number from 1-65000, called an assembly instance id.
# These ids are then used in the two sections named
# target_role_connections and originator_role_connections which follow.
#
# (my_assemblies                            ` gives the CIP assemblies that the
#                                           ` TLM should create
#   <(my_producing ...) |
#    (my_consuming ...) |
#    (my_cfg ...) |
#    (my_explict ...)> ...
#   ) /* end of my_assemblies */
#
#   (my_producing <ASSEMBLY_ID>             ` flows from the datatable onto the network
#     (data <int16 | float32>)(elements <ELEMENT_COUNT>)(tag <TAG_NAME>)
#     )
#   (my_consuming <ASSEMBLY_ID>             ` flows from network into datatable
#     (data <int16 | float32>)(elements <ELEMENT_COUNT>)(tag <TAG_NAME>)
#     )
#   (my_config <ASSEMBLY_ID>                ` is bidirectional with datatable
#     (data <int16 | float32>)(elements <ELEMENT_COUNT>)(tag <TAG_NAME>)
#     )
#   (my_explicit <ASSEMBLY_ID>              ` is bidirectional with datatable
#     (data <int16 | float32>)(elements <ELEMENT_COUNT>)(tag <TAG_NAME>)
#     )
#
# Section (target_role_connections ...) is a list of i/o connections that should
# be accepted should we see an inbound forward_open containing a matching
# combination of assembly ids.
# (my_consuming ...) and (my_producing ...) and (my_config ...) are from
# my perspective within this network node.
#
# (target_role_connections
#   (expect exclusive_owner                 ` which kind of i/o connection, only
#                                           ` exclusive_owner for now
#     (my_consuming 150)                    ` which consuming assembly id
#     (my_producing 100)                    ` which producing assembly id
#     [(my_config 151)]                     ` optional config assembly id
#     )
#   (expect exclusive_owner (my_consuming 150)(my_producing 100))
#   )   /* end of target_role_connections */
#
#
# Section (originator_role_connections ...) lists client connections which
# will be made and manageed as a scanner.
# (originator_role_connections
#   (target ...)
#   [(target ...)] ...
#   )   /* end of originator_role_connections */
#
# 'target' is a TCP node that is capable of being an Ethernet/IP explicit message
#  server that supports the forward_open command.  One or more forward_open
#  commands will be sent to this node in order to setup the requested number
#  of io_connections, which are all UDP based.
#
# (target <IP_ADDRESS | HOSTNAME>           ` HOSTNAME ok if findable via DNS,
#                                           ` else provide an IP_ADDRESS
#   [(tcp_connect_timeout_ms <MSECS>)]      ` TCP connect() time limit of
#                                           ` originating : 5000 is default
#   [(tcp_connect_retry_delay_ms <MSECS>)]  ` How long after failed TCP connection
#                                           ` to try again. : 5000 is default
#   (io_connection ...)
#   [(io_connection ...)] ...
#   )   /* end of target */
#
# (io_connection
#   [(conn_path ...)]                       ` Hex bytes in CIP segment form to
#                                           ` precede the 1-3 Application Paths.
#                                           ` If used at all, probably just a port path.
#   [(fwd_open_timeout_ms <MSECS>)]         ` how long client waits for server after
#                                           ` sending forward_open : 1000 is default
#   [(io_timeout_mult <4|8|16|32|64|128|256|512> )] `see CIP docs for forward_open.
#                                           ` Actual io_connection timeout is unique for
#                                           ` each connection half, and is rpi_usecs times
#                                           ` this number : 4 is default
#   [<cyclic | change_of_state>]            ` CIP connection trigger
#                                           ` type : cyclic is default
#   [<class0 | class1>]                     ` CIP transport class : class1 is default
#   [(config ...)]                          ` configuration one shot transfer
#   [(o_t ...)]                             ` originator to target connection half,
#                                           ` if missing means no o->t transfer
#   [(t_o ...)]                             ` target to originator connection half,
#                                           ` if missing means no t->o transfer
#   )   /* end of io_connection */
#
# (config
#   (my_config <ASSEMBLY_ID>)               ` A block of data to send to the target,
#                                           ` or it can be zero length also.
#   [(tgt_config <ASSEMBLY_ID>)]            ` Which assembly is to receive it.
#                                           ` May be omitted if this info is in (conn_path ...)
#   [(size_bytes <NUM_BYTES>)]              ` If missing then the size of
#                                           ` my_config will be used.
#   )
#
# (o_t                                      ` originator to target connection half
#   (rpi_us <NUM_USECS>)                    ` Requested Packet Interval in usecs not msecs
#   [<p_to_p | multicast>]                  ` peer to peer or multicast
#                                           ` : p_to_p is default
#   [(rt_fmt <32_bit_header | modeless>)]   ` CIP Vol1 3-6.1
#                                           ` : "32_bit_header" is default for o_t
#   (my_producing <ASSEMBLY_ID>)            ` my assembly which is the producer
#   [(tgt_consuming <ASSEMBLY_ID>)]         ` the consuming assembly number within
#                                           ` the target
#   [(size_bytes <NUM_BYTES>)]              ` If zero then this is a heartbeat half.
#                                           ` If missing then the size of
#                                           ` my_producing will be used.
#   )   /* end of o_t */
#
# (t_o                                      ` target to originator connection half
#   (rpi_us <NUM_USECS>)                    ` Requested Packet Interval in usecs not msecs
#   [<p_to_p | multicast>]                  ` peer to peer or multicast
#                                           ` : p_to_p is default
#   [(rt_fmt <32_bit_header | modeless>)]   ` CIP Vol1 3-6.1
#                                           ` : "modeless" is default for t_o
#   (my_consuming <ASSEMBLY_ID>)            ` my assembly which is the consumer
#   [(tgt_producing <ASSEMBLY_ID>)]         ` the producing assembly number
#                                           ` within the target
#   [(size_bytes <NUM_BYTES>)]              ` If zero then this is a heartbeat half.
#                                           ` If missing then the size of
#                                           ` my_consuming will be used.
#   )   /* end of t_o */
#
# end of intro comments and grammar rules.
# Next comes actual configuration statements:

(ethernet_ip
  (driver_version 1)
  (interface eth0)
  #(debug 0x3f)
  (debug 0)

  (my_assemblies
    # 3 assemblies for originator_role_connections
    (my_producing   1   (data int16)(elements   6)(tag "TO_DRIVE"))
    (my_consuming   2   (data int16)(elements   6)(tag "FROM_DRIVE"))
    (my_config      3   (data int16)(elements   0)(tag "DRIVE_CONFIG"))

    # 5 assemblies for target_role_connections
    #   first expected connection:
    (my_producing   100 (data int16)(elements  64)(tag "TO_PLC"))
    (my_consuming   150 (data int16)(elements  64)(tag "FROM_PLC"))
    (my_config      151 (data int16)(elements  32)(tag "CONFIG_FOR_ME"))
    #   second expected connection:
    (my_producing   200 (data int16)(elements 100)(tag "TO_HMI"))
    (my_consuming   201 (data int16)(elements 100)(tag "FROM_HMI"))
    )

  (target_role_connections
    (expect exclusive_owner (my_consuming 150)(my_producing 100)(my_config 151))
    (expect exclusive_owner (my_consuming 201)(my_producing 200))
    )

  (originator_role_connections
    (target 192.100.100.3
      (io_connection
        (config (my_config 3)(tgt_config 103))
        (t_o (rpi_us 150000)(my_consuming 2)(tgt_producing 101))
        (o_t (rpi_us 150000)(my_producing 1)(tgt_consuming 102))
        )
      )
    )

  )

5. Operation

There are 3 Topdoc Loadable Instructions (TLI)s that can be called from Ladder Logic to monitor and control the operation of Ethernet/IP TLM.

5.1. EIP_GETFAULTMAP

This TLI is a permissive instruction that evaluates to either true or false depending on whether there are any (io_connections …​) which are in an unconnected state. That means that the output logic to the right of this permissive will be energized when there is some kind of communictions failure. So it acts like an alarm.

Additionally, the datatable block given by the Map parameter will be filled with a bitmap containing one bit for each io_connection, and will turn on bits which represent the io_connections which are failed. Bit 0 of the first word at Map represents the first (io_connection …​) in the configuration file, bit 1 of the first word at Map represents the second (io_connection …​), etc. All io_connections across all targets are in this set. The length of the Map block is 128 bits fixed length. This is 128/16 = 8 words.

eip getfaultmap

5.2. EIP_GETSTATS

This TLI is an output instruction that retrieves a block of 16 bit words into the Result parameter. Each word in the Result block is a count of how many times the TLM has lost contact with the respective io_connection.

Each instance of this instruction may only deal with the io_connections under one target, given by the Target parameter. In the Target STring you should put the same IP_ADDRESS or machine name that you used in the target’s configuration.

The io_connections defined within the configured target are implicitly numbered starting with index 0. The IoConnIndex is the start of a consecutive range of target io_connection indices, and will normally be 0 to indicate that you want the first io_connection under Target. IoConnCount is the number of io_connections that you want to fetch starting at IoConnIndex. It is the length of the range, and may not go beyond the number of io_connections given in the configuration file for Target, minus the starting IoConnIndex.

You will need a separate instance of this instruction for each target.

eip getstats

5.3. EIP_CLEARSTATS

This TLI is an output instruction that zeros one 16 bit counter word for each (io_connection …​) associated with a particular (target …​) given by the Target parameter. In the Target STring you should put the same IP_ADDRESS or machine name that you used in the target’s configuration.

The io_connections defined within the configured target are implicitly numbered starting with index 0.

You will need a separate instance of this instruction for each target.

eip clearstats