> For the complete documentation index, see [llms.txt](https://docs.cybus.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cybus.io/2-4-2/guides/machine-connectivity/plcs-industrial-controllers/connecting-a-wago-pfc200-controller.md).

# Connecting a WAGO PFC200 Controller

This guide shows you how to read PLC variables from a WAGO PFC200 controller with the Connectware [Modbus/TCP connector](/2-4-2/connectors/shop-floor-connectors/modbus-tcp.md) and map them into an ISA-95 style MQTT topic hierarchy. The PFC200 is a modular controller of the WAGO I/O System 750 that runs a CODESYS V3 based runtime, and its built-in Modbus/TCP server is a robust way to get PLC data into Connectware without touching the fieldbus side of the application. In more detail, the following topics are covered:

* Making PLC variables Modbus-readable in the CODESYS project
* Choosing register offsets and the matching function code
* Decoding 16-bit and 32-bit values with the correct `dataType`
* Creating the [service commissioning file](/2-4-2/data-flows/service-commissioning-files.md)
* Mapping the values into an ISA-95 style MQTT topic hierarchy
* Verifying data in the [Data Explorer](/2-4-2/monitoring/data-explorer.md)

This guide focuses on the PFC200 specifics. For a general introduction to the Modbus/TCP connector, including function codes and write operations, see [Connecting & Integrating a Modbus/TCP Server](/2-4-2/guides/machine-connectivity/gateways-generic-protocols/connecting-and-integrating-a-modbus-tcp-server.md).

A complete example file is available at the end of this guide.

## Prerequisites

To follow this guide, you will need the following:

* A running instance of Cybus Connectware.
* A WAGO PFC200 controller (750-82xx) that is reachable over Ethernet from Connectware and runs the CODESYS V3 based runtime (e!RUNTIME).
* Access to the CODESYS project of the controller (CODESYS 3.5 or e!COCKPIT), because the Modbus register map of a PFC200 is defined in the project.
* Access to the [Admin UI](/2-4-2/access/admin-ui.md) with sufficient [user permissions](/2-4-2/access/user-management.md).
* Basic knowledge of MQTT and the Connectware [services](/2-4-2/data-flows/services.md) concept (for example, [service commissioning files](/2-4-2/data-flows/service-commissioning-files.md), [connections](/2-4-2/data-flows/service-commissioning-files/resources/cybus-connection.md), and [endpoints](/2-4-2/data-flows/service-commissioning-files/resources/cybus-endpoint.md)).

{% hint style="info" %}
First-generation PFC200 controllers can also run the legacy CODESYS 2.3 runtime. That runtime exposes a fixed, firmware-defined Modbus process image instead of a project-defined one. The address ranges are documented in the product manual of your controller, available in the [WAGO PFC200 product documentation](https://www.wago.com/us/pfc200). The Connectware side of this guide applies unchanged; only the register offsets come from the manual instead of your CODESYS project.
{% endhint %}

## Making PLC Variables Modbus-Readable

Unlike a device with a fixed register table, such as a power meter, the PFC200 has no predefined Modbus registers for your application data. You define the register map yourself in the CODESYS project by adding a Modbus/TCP server device and assigning PLC variables to its registers. CODESYS uses the terms master and slave for the Modbus client and server roles: the PFC200 acts as the server (slave), and Connectware is the client (master).

To publish PLC variables over Modbus/TCP:

1. In the CODESYS device tree, right-click the PFC200 and select **Add Device**.
2. Add an **Ethernet** adapter from the **Ethernet Adapter** category.
3. Right-click the new Ethernet device and add a **Modbus TCP Slave Device** from the **Modbus** category.
4. Double-click the new device to open its configuration and set the sizes of the holding register and input register areas. Leave the port at the default 502.
5. Open the **Modbus TCP Slave Device I/O Mapping** tab and enter a PLC variable for each register the controller should expose. Registers that the controller fills for Connectware to read belong in the input register area. The holding register area is meant for data written by the client.
6. Download the application to the controller and start it. The Modbus/TCP server is part of the application, so it only accepts connections on port 502 while the application is running.

WAGO describes this workflow in detail in the [Modbus example project in the WAGO technical documentation](https://techdocs.wago.com/Software/eCOCKPIT_Migration/en-US/2326433803.html).

The register offsets on the I/O mapping tab start at 0 and map directly to the `address` property of a Connectware endpoint. Input registers are read with function code 4, holding registers with function code 3.

### Example Register Map

The register map is specific to your project, so this guide works with the following example mapping. Replace the variables, offsets, and data types with the ones from your I/O mapping tab.

| Offset | Number of registers | CODESYS variable    | IEC type | `dataType`  |
| -----: | ------------------: | ------------------- | -------- | ----------- |
|      0 |                   1 | `wMachineState`     | WORD     | `uint16BE`  |
|      1 |                   1 | `iLineSpeed`        | INT      | `int16BE`   |
|      2 |                   2 | `rMotorTemperature` | REAL     | `floatBEWS` |
|      4 |                   2 | `rAirPressure`      | REAL     | `floatBEWS` |

### Decoding Multi-Register Values

Modbus registers are 16 bits long. WORD, INT, and UINT variables fit into a single register, and their `dataType` follows directly from the IEC type: `uint16BE` for unsigned values, `int16BE` for signed values.

A REAL (IEEE 754, 32-bit) spans two registers, and here the word order matters: the Modbus protocol defines big-endian byte order within each register, but not which of the two registers carries the high word. On a PFC200, the CODESYS runtime typically places the low word at the lower register offset, which corresponds to `dataType: floatBEWS` (big-endian with word swap) in Connectware. Because the word order depends on the runtime and how the value is written in the project, verify it against a known value: set the variable to a constant such as `1234.5` in the PLC and check the published value. If the value arrives extremely large, near zero, or otherwise implausible, switch between `floatBEWS` and `floatBE`. For all available data types, see [Modbus/TCP Endpoint Properties](/2-4-2/connectors/shop-floor-connectors/modbus-tcp/modbusendpoint.md).

## Writing the Service Commissioning File

The service commissioning file contains all connection and mapping details. Do not worry about copying the snippets together into one file, the complete example file is available at the end of this guide.

### Description and Metadata

These sections contain general information about the service commissioning file. Only the metadata name is required.

{% code lineNumbers="true" %}

```yaml
description: >
  Service commissioning file that reads PLC variables from a WAGO PFC200
  controller over Modbus/TCP and maps them into an ISA-95 style MQTT topic
  hierarchy (Example)

metadata:
  name: WAGO PFC200 Example
  provider: cybus
  homepage: https://www.cybus.io
  version: 1.0.0
```

{% endcode %}

### Parameters and Definitions

We define the network address of the controller as parameters, so you can set them when you install the service. The default Modbus/TCP port is 502.

The MQTT topics in this guide follow an ISA-95 style equipment hierarchy (`<enterprise>/<site>/<area>/<line>`). We define the prefix once in the `definitions` section and reuse it in every mapping with `!sub`.

{% code lineNumbers="true" %}

```yaml
parameters:
  pfc200Host:
    description: Hostname or IP address of the WAGO PFC200
    type: string
    default: 192.168.1.100

  pfc200Port:
    description: Modbus/TCP port of the WAGO PFC200
    type: integer
    default: 502

definitions:
  MQTT_TOPIC_PREFIX: enterprise/hamburg/packaging/line-2
```

{% endcode %}

### Cybus::Connection

The connection resource establishes the Modbus/TCP connection to the controller. The `connectionStrategy` object controls how Connectware retries failed connection attempts with increasing delays. The default unit ID 1 works for a PFC200 that you address directly; if you reach the controller through a Modbus gateway, set the `unitId` connection property accordingly. For all connection properties, see [Modbus/TCP Connection Properties](/2-4-2/connectors/shop-floor-connectors/modbus-tcp/modbusconnection.md).

{% code lineNumbers="true" %}

```yaml
resources:
  pfc200Connection:
    type: Cybus::Connection
    properties:
      protocol: Modbus
      targetState: connected
      connection:
        host: !ref pfc200Host
        port: !ref pfc200Port
        connectionStrategy:
          initialDelay: 1000
          maxDelay: 30000
          incrementFactor: 2
```

{% endcode %}

### Cybus::Endpoint

Each endpoint polls one PLC variable. The `address` and `length` values come directly from the example register map: the offset becomes `address`, the number of registers becomes `length`. All values are in the input register area, so every endpoint reads with function code 4. The state and speed values are polled every second, the slower analog values every two seconds.

{% code lineNumbers="true" %}

```yaml
machineState:
  type: Cybus::Endpoint
  properties:
    protocol: Modbus
    connection: !ref pfc200Connection
    subscribe:
      fc: 4
      address: 0
      length: 1
      dataType: uint16BE
      interval: 1000

lineSpeed:
  type: Cybus::Endpoint
  properties:
    protocol: Modbus
    connection: !ref pfc200Connection
    subscribe:
      fc: 4
      address: 1
      length: 1
      dataType: int16BE
      interval: 1000

motorTemperature:
  type: Cybus::Endpoint
  properties:
    protocol: Modbus
    connection: !ref pfc200Connection
    subscribe:
      fc: 4
      address: 2
      length: 2
      dataType: floatBEWS
      interval: 2000

airPressure:
  type: Cybus::Endpoint
  properties:
    protocol: Modbus
    connection: !ref pfc200Connection
    subscribe:
      fc: 4
      address: 4
      length: 2
      dataType: floatBEWS
      interval: 2000
```

{% endcode %}

{% hint style="info" %}
If you poll many endpoints, enable [batch read processing](/2-4-2/connectors/shop-floor-connectors/modbus-tcp.md#batch-read-processing) on the connection. Connectware then combines reads of neighboring registers, such as the contiguous offsets 0 to 5 in this example, into a single request.
{% endhint %}

### Cybus::Mapping

The mapping publishes each endpoint on a topic of the ISA-95 hierarchy.

{% code lineNumbers="true" %}

```yaml
mapping:
  type: Cybus::Mapping
  properties:
    mappings:
      - subscribe:
          endpoint: !ref machineState
        publish:
          topic: !sub '${MQTT_TOPIC_PREFIX}/state'
      - subscribe:
          endpoint: !ref lineSpeed
        publish:
          topic: !sub '${MQTT_TOPIC_PREFIX}/line-speed'
      - subscribe:
          endpoint: !ref motorTemperature
        publish:
          topic: !sub '${MQTT_TOPIC_PREFIX}/temperature/motor'
      - subscribe:
          endpoint: !ref airPressure
        publish:
          topic: !sub '${MQTT_TOPIC_PREFIX}/pressure/air'
```

{% endcode %}

With this mapping, the motor temperature of the line is published on the topic `enterprise/hamburg/packaging/line-2/temperature/motor`, and every other value follows the same pattern.

## Installing the Service Commissioning File

1. Install the service commissioning file. See [Installing Services](/2-4-2/data-flows/services/managing/installing.md).
2. Enable the service. See [Enabling Services](/2-4-2/data-flows/services/managing/enabling.md).

**Result:** The service is enabled. Connectware establishes the Modbus/TCP connection to the PFC200 and polls the configured registers.

## Verifying the Data

Open the [Data Explorer](/2-4-2/monitoring/data-explorer.md) and subscribe to `enterprise/hamburg/packaging/line-2/#`. Each topic carries a JSON object with the keys `timestamp` and `value`, where the value is the decoded PLC variable:

{% code lineNumbers="true" %}

```json
{
  "timestamp": 1784194200354,
  "value": 62.5
}
```

{% endcode %}

A few plausibility checks for the first readings:

* The machine state is a small integer code that matches the state shown by the machine or its HMI.
* The REAL values are in a physically plausible range, for example a motor temperature between 20 and 100 degrees Celsius.
* Forcing a variable to a known constant in CODESYS changes the published value accordingly.

If the connection does not reach the **Connected** state, verify that the CODESYS application with the Modbus/TCP server device is running on the controller and that port 502 is reachable from Connectware; the firewall settings in the Web-Based Management (WBM) of the PFC200 can block the port. If values arrive but look implausible (for example, extremely large or near-zero numbers for a REAL), the register decoding does not match: check that `address`, `length`, and `dataType` correspond to one row of your I/O mapping, and switch between the word order variants as described in [Decoding Multi-Register Values](#decoding-multi-register-values).

## Service Commissioning File Example

{% file src="/files/bIPgmyxvftxJjbSgpeco" %}

{% code title="wago-pfc200-example.yml" lineNumbers="true" expandable="true" %}

```yaml
---
# ----------------------------------------------------------------------------
# Service Commissioning File
# ----------------------------------------------------------------------------
# Copyright: Cybus GmbH
# Contact: support@cybus.io
# ----------------------------------------------------------------------------
# Connecting a WAGO PFC200 Controller (Example)
# ----------------------------------------------------------------------------

description: >
  Service commissioning file that reads PLC variables from a WAGO PFC200
  controller over Modbus/TCP and maps them into an ISA-95 style MQTT topic
  hierarchy (Example)

metadata:
  name: WAGO PFC200 Example
  provider: cybus
  homepage: https://www.cybus.io
  version: 1.0.0

parameters:
  pfc200Host:
    description: Hostname or IP address of the WAGO PFC200
    type: string
    default: 192.168.1.100

  pfc200Port:
    description: Modbus/TCP port of the WAGO PFC200
    type: integer
    default: 502

definitions:
  # ISA-95 style topic prefix: <enterprise>/<site>/<area>/<line>
  MQTT_TOPIC_PREFIX: enterprise/hamburg/packaging/line-2

resources:
  # Connection to the WAGO PFC200 (Modbus/TCP server of the CODESYS runtime)
  pfc200Connection:
    type: Cybus::Connection
    properties:
      protocol: Modbus
      targetState: connected
      connection:
        host: !ref pfc200Host
        port: !ref pfc200Port
        connectionStrategy:
          initialDelay: 1000
          maxDelay: 30000
          incrementFactor: 2

  # The register offsets below are project-specific examples. They come from
  # the Modbus TCP Slave Device I/O Mapping tab of the CODESYS project, where
  # PLC variables are assigned to input registers (read with function code 4).
  machineState:
    type: Cybus::Endpoint
    properties:
      protocol: Modbus
      connection: !ref pfc200Connection
      subscribe:
        fc: 4
        address: 0
        length: 1
        dataType: uint16BE
        interval: 1000

  lineSpeed:
    type: Cybus::Endpoint
    properties:
      protocol: Modbus
      connection: !ref pfc200Connection
      subscribe:
        fc: 4
        address: 1
        length: 1
        dataType: int16BE
        interval: 1000

  # REAL values span two registers. The CODESYS runtime on the PFC200
  # typically places the low word first, which matches floatBEWS (big-endian
  # with word swap). If the values look implausible, switch to floatBE.
  motorTemperature:
    type: Cybus::Endpoint
    properties:
      protocol: Modbus
      connection: !ref pfc200Connection
      subscribe:
        fc: 4
        address: 2
        length: 2
        dataType: floatBEWS
        interval: 2000

  airPressure:
    type: Cybus::Endpoint
    properties:
      protocol: Modbus
      connection: !ref pfc200Connection
      subscribe:
        fc: 4
        address: 4
        length: 2
        dataType: floatBEWS
        interval: 2000

  mapping:
    type: Cybus::Mapping
    properties:
      mappings:
        - subscribe:
            endpoint: !ref machineState
          publish:
            topic: !sub '${MQTT_TOPIC_PREFIX}/state'
        - subscribe:
            endpoint: !ref lineSpeed
          publish:
            topic: !sub '${MQTT_TOPIC_PREFIX}/line-speed'
        - subscribe:
            endpoint: !ref motorTemperature
          publish:
            topic: !sub '${MQTT_TOPIC_PREFIX}/temperature/motor'
        - subscribe:
            endpoint: !ref airPressure
          publish:
            topic: !sub '${MQTT_TOPIC_PREFIX}/pressure/air'
```

{% endcode %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.cybus.io/2-4-2/guides/machine-connectivity/plcs-industrial-controllers/connecting-a-wago-pfc200-controller.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
