README
¶
Get started
Get the required modules for benthos. Compile the example files Run the example file. in Windows: ./example.exe -c benthos.yaml
ads
Input for Beckhoffs ads protocol. Supports batch reading and notifications. Beckhoff recommends not using more than about 500 notifications due to the impact of the controller. This input only supports symbols and not direct addresses
---
input:
ads:
targetIP: '192.168.3.70' # IP address of the PLC
targetAMS: '5.3.69.134.1.1' # AMS net ID of the target
targetPort: 48898 # Port of the target internal gateway
runtimePort: 801 # Runtime port of PLC system
hostAMS: '192.168.56.1.1.1' # Host AMS net ID. Usually the IP address + .1.1
readType: 'interval' # Read type, interval or notification
maxDelay: 100 # Max delay for sending notifications in ms
cycleTime: 100 # Cycle time for notification handler in ms
intervalTime: 1000 # Interval time for reading in ms
upperCase: true # Convert symbol names to all uppercase for older PLCS
logLevel: "disabled" # Log level for ADS connection
symbols: # List of symbols to read from
- "MAIN.MYBOOL" # variable in the main program
- "MAIN.MYTRIGGER:0:10" # variable in the main program with 0ms max delay and 10ms cycleTime
- "MAIN.SPEEDOS"
- ".superDuperInt" # Global variable
- ".someStrangeVar"
pipeline:
processors:
- bloblang: |
root = {
meta("symbol_name"): this,
"timestamp_ms": (timestamp_unix_nano() / 1000000).floor()
}
output:
stdout: {}
logger:
level: ERROR
format: logfmt
add_timestamp: true
Connection to ADS
When connecting to an ADS device you connect to a router which then routes the traffic to the correct device using the AMS net ID. There are basically 3 ways for setting up the connection:
- TwinCAT Connection Manager: Use the TwinCAT connection manager locally on the host, scan for the device and add a connection using the correct credentials for the PLC.
- Static route on PLC: Log in to the PLC using the TwinCAT system manager and add a static route from the PLC to the client. This is the preferred way when using benthos on a Kubernetes cluster since you have no good way of installing the connection manager.
- Automatic route registration (UDP): Use the
routeUsernameandroutePasswordconfig fields to have the plugin automatically register a route on the PLC before connecting. See the Route Registration section below.
Docker and Kubernetes
ADS works from inside Docker containers with default bridge networking — no host_network, no port forwarding, and no open ports are needed. All ADS traffic (requests, responses, and notifications) flows over a single outbound TCP connection to port 48898. The PLC never initiates connections back to the client; it sends all responses and notifications on the same TCP socket the client opened.
The only requirement is that the hostAMS value matches a route registered on the PLC. When running in Docker with bridge networking:
routeHostAddressmust be set to the Docker host's IP on the PLC network (e.g.192.168.1.50). This tells the PLC which IP address to associate with the route. If left empty, it auto-detects the container's bridge IP which is not routable from the PLC.hostAMScan be set explicitly torouteHostAddress+.1.1(e.g.192.168.1.50.1.1), or left asauto— when route registration is configured withrouteHostAddress,autowill correctly derive the AMS NetID fromrouteHostAddressinstead of the container's bridge IP.- A route must exist on the PLC for the
hostAMSNetID. This can be added manually in TwinCAT System Manager, or automatically via therouteUsername/routePasswordconfig fields. hostPortis optional (default 10500). It is a logical AMS port used in protocol headers, not a network port. Any value works.
Option A: Automatic route registration (recommended)
The plugin registers a route on the PLC automatically via UDP before connecting. No manual PLC configuration needed:
input:
ads:
targetIP: '192.168.1.100'
targetAMS: '192.168.1.100.1.1'
runtimePort: 851
hostAMS: 'auto' # Derives AMS NetID from routeHostAddress
routeUsername: 'Administrator' # Triggers automatic route registration
routePassword: '1'
routeHostAddress: '192.168.1.50' # Docker HOST IP (required in bridge networking)
readType: 'notification'
symbols:
- "MAIN.MyVariable"
You can also set hostAMS explicitly if you prefer:
hostAMS: '192.168.1.50.1.1' # Explicit: Docker HOST IP + .1.1
routeHostAddress: '192.168.1.50' # Must match
Option B: Static route on PLC
If you prefer not to use automatic registration, add a static route on the PLC via TwinCAT System Manager pointing to the Docker host's IP. Then configure hostAMS to match — no routeUsername/routePassword needed:
input:
ads:
targetIP: '192.168.1.100'
targetAMS: '192.168.1.100.1.1'
runtimePort: 851
hostAMS: '192.168.1.50.1.1' # Must match the route on the PLC
readType: 'notification'
symbols:
- "MAIN.MyVariable"
Option C: host_network or macvlan
When the container has a routable IP on the PLC network, hostAMS: auto works without routeHostAddress:
input:
ads:
targetIP: '192.168.1.100'
targetAMS: '192.168.1.100.1.1'
runtimePort: 851
hostAMS: 'auto' # Auto-derive from container's real IP
routeUsername: 'Administrator' # Optional: auto-register route
routePassword: '1'
readType: 'notification'
symbols:
- "MAIN.MyVariable"
Configuration Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
| targetIP | Yes | — | IP address of the Beckhoff PLC |
| targetAMS | Yes | — | AMS net ID of the target |
| symbols | Yes | — | List of symbols to read from (see Symbol Format below) |
| targetPort | No | 48898 |
Port of the target internal gateway |
| runtimePort | No | 801 |
Runtime port of PLC system, 800–899. TwinCAT 2 uses 800–850 (usually 801), TwinCAT 3 uses 851–899 (usually 851) |
| hostAMS | No | auto |
Host AMS net ID. Usually the IP address + .1.1. Must match a route on the PLC. auto derives it from routeHostAddress if set, otherwise from the outbound connection's local IP |
| hostPort | No | 10500 |
AMS source port used in protocol headers. This is a logical port, not a network port. Any arbitrary value works |
| readType | No | notification |
Read type for the symbols. interval polls at intervalTime; notification uses PLC push updates (see Interval vs Notification) |
| maxDelay | No | 100 |
Default max delay for sending notifications in ms. Maximum time after value change before PLC must send the notification |
| cycleTime | No | 1000 |
Default cycle time for notification handler in ms. How often the PLC scans for changes. Use a low value for triggers that are only true/false for 1 PLC cycle |
| intervalTime | No | 1000 |
Interval time between reads in ms (only used when readType is interval) |
| requestTimeout | No | 5000 |
Timeout for individual ADS requests in ms. Increase for slow PLCs or large symbol tables |
| transmissionMode | No | serverOnChange |
Notification transmission mode. Only applies when readType is notification. Options: serverOnChange, serverCycle, serverOnChange2, serverCycle2 (see Transmission Modes) |
| upperCase | No | true |
Convert symbol names to all uppercase. Often necessary for TwinCAT 2 PLCs |
| logLevel | No | disabled |
Log level for ADS connection (disabled, error, warn, info, debug, trace). At debug/trace, ADS error codes show human-readable descriptions |
| routeUsername | No | "" |
Username for automatic UDP route registration on the PLC. If set, a route is registered before connecting (see Route Registration) |
| routePassword | No | "" |
Password for automatic UDP route registration on the PLC |
| routeHostAddress | No | "" |
IP address the PLC associates with the route. Required in Docker bridge networking (set to Docker host's IP). When hostAMS is auto, the AMS NetID is also derived from this. Auto-detected from outbound connection if empty (only correct with host_network or macvlan) |
Symbol Format
Symbols are specified in the format function.variable:maxDelay:cycleTime:
MAIN.MYBOOL— variable in the main program, uses default maxDelay and cycleTimeMAIN.MYTRIGGER:0:10— variable with 0ms max delay and 10ms cycle time.superDuperInt— global variable (must start with.)
Transmission Modes
Note:
transmissionModeonly applies whenreadTypeisnotification. When usingreadType: interval, the plugin sends plain ADS Read commands to the PLC at each interval — no notification mechanism is involved, andtransmissionModeis ignored.
The transmissionMode field controls how the PLC's internal notification handler sends updates back to the client. The available modes are:
| Mode | Value | Description |
|---|---|---|
serverOnChange |
4 | (Default) The PLC scans for changes at the configured cycleTime interval and sends a notification only when the value has changed. This is the most efficient mode for most use cases. |
serverCycle |
3 | The PLC sends the current value at every cycleTime interval, regardless of whether the value has changed. Useful when you need a constant data stream or heartbeat. |
serverOnChange2 |
6 | Enhanced version of serverOnChange available on newer TwinCAT 3 firmware. Supports more efficient internal handling on the PLC side. Automatically falls back to serverOnChange on older PLCs. |
serverCycle2 |
5 | Enhanced version of serverCycle available on newer TwinCAT 3 firmware. Same behavior as serverCycle but with improved internal efficiency. Automatically falls back to serverCycle on older PLCs. |
Choosing a mode:
- Use
serverOnChange(default) for event-driven data where you only care about changes - Use
serverCyclewhen you need periodic snapshots regardless of changes - The
2variants (serverOnChange2,serverCycle2) can be used safely on any PLC — the plugin automatically detects older PLCs and falls back to the v1 equivalent
input:
ads:
transmissionMode: 'serverOnChange' # default, sends only on value change
# transmissionMode: 'serverCycle' # sends at every cycle regardless of change
# transmissionMode: 'serverOnChange2' # enhanced, auto-falls back on older PLCs
# transmissionMode: 'serverCycle2' # enhanced cyclic, auto-falls back on older PLCs
Interval vs Notification
The interval and notification read types can produce similar-looking results (periodic data), but they work differently under the hood:
interval: The client polls the PLC — sends an ADS Read request for each symbol at everyintervalTimeinterval. Simple, no PLC notification overhead, and not subject to the ~500-notification limit.notification+serverOnChange: The PLC pushes data only when a value changes. Most efficient for event-driven data. Subject to the ~500-notification limit per connection.notification+serverCycle: The PLC pushes data at everycycleTimeinterval regardless of changes. Similar result tointervalbut PLC-driven — more precise timing with no request/response overhead per cycle. Subject to the ~500-notification limit.
| Aspect | interval |
notification + serverOnChange |
notification + serverCycle |
|---|---|---|---|
| Who drives | Client polls | PLC pushes on change | PLC pushes on timer |
| Network per cycle | Request + response | Push only | Push only |
| Sends unchanged values | Yes | No | Yes |
| Timing precision | Subject to network latency | PLC real-time task | PLC real-time task |
| PLC notification limit | No limit | ~500 max | ~500 max |
| Best for | Large symbol lists, simple setup | Event-driven data (most use cases) | Precise periodic sampling |
Route Registration
The plugin can automatically register a route on the PLC using the Beckhoff UDP route protocol (port 48899). This removes the need to manually add routes in the TwinCAT System Manager.
How it works:
- Before establishing the TCP connection, the plugin sends a UDP route registration packet to port 48899 on the PLC
- The packet tells the PLC: "Associate AMS NetID X with IP address Y"
- The PLC adds this as a runtime route (may not be visible in TwinCAT System Manager)
- The normal ADS TCP connection is then established over port 48898
Setting routeUsername activates automatic route registration. If route registration fails (e.g. UDP response lost due to NAT), the plugin logs a warning and still attempts the TCP connection — the route may have been created despite the missing response.
Parameters:
routeUsername/routePassword: PLC administrator credentials. Same as used in TwinCAT System Manager to add routesrouteHostAddress: The IP address the PLC associates with this client. In Docker with bridge networking, this must be set to the Docker host's IP on the PLC network. WhenhostAMSisauto, the AMS NetID is derived from this address. Auto-detected if empty (only correct withhost_networkor macvlan)
Network requirements:
- UDP port 48899 must be reachable on the PLC from the client (for route registration)
- TCP port 48898 must be reachable on the PLC from the client (outbound — works through any NAT)
Reconnection
The plugin automatically reconnects when the TCP connection is lost (e.g. network cable unplugged, PLC restart). Aggressive TCP keepalive probes detect dead connections within ~13 seconds. On reconnect, the plugin:
- Re-establishes the TCP connection (retries indefinitely with 5s interval)
- Reloads the symbol table from the PLC
- Re-subscribes all notification handles
No manual intervention is needed.
Output
This outputs for each address a single message with the payload being the value that was read. To distinguish messages, you can use meta("symbol_name") in a following benthos bloblang processor.
Testing
Tested and verified:
CX7000, TwinCAT 3
- Notifications from Docker container with bridge networking (no host_network)
- Automatic UDP route registration from Docker bridge networking
- Static route with explicit hostAMS (no route registration)
- Reconnection after network loss with automatic notification re-subscribe
- Sum/batch commands for read, add notification, and delete notification
CX1020, TwinCAT 2
- Read batches, Add notifications, different cycle times and max delay
- Different datatypes, INT, INT16, UINT, DINT, BOOL, STRUCT, and more
- Automatic fallback from sum commands to individual calls
- Automatic fallback from v2 transmission modes to v1
- Reconnection after network loss with automatic notification re-subscribe
Documentation
¶
There is no documentation for this package.