s7commctl
s7commctl is a command-line S7comm client for ISO-on-TCP (TCP port 102), built on go-s7comm. It is meant for commissioning, troubleshooting and engineering work on PLCs you are responsible for.
Install
Section titled “Install”Prebuilt binaries are attached to each GitHub release: .tar.gz archives for Linux and macOS and .zip archives for Windows, for amd64 and arm64.
To build from source you need Go 1.25:
git clone https://github.com/otfabric/s7commctlcd s7commctlmake build # writes bin/s7commctlCheck the build with s7commctl version, which prints the version, commit and build date.
Connecting
Section titled “Connecting”Every command that talks to a PLC takes --ip, and --port when the PLC is not on the default port 102. Pick the CPU with --rack and --slot. s7commctl also supports automatic rack/slot selection and explicit local and remote TSAPs; the JSON output records which of the three connection modes (rack_slot, auto_rack_slot or explicit_tsap) a command used.
Most flags can also come from S7COMMCTL_* environment variables, for example S7COMMCTL_IP, S7COMMCTL_PORT, S7COMMCTL_RACK, S7COMMCTL_SLOT, S7COMMCTL_TIMEOUT, S7COMMCTL_AUTO_RACK_SLOT, S7COMMCTL_LOCAL_TSAP and S7COMMCTL_REMOTE_TSAP:
export S7COMMCTL_IP=192.168.1.10export S7COMMCTL_RACK=0export S7COMMCTL_SLOT=1s7commctl identifys7commctl warns when the target address looks publicly routable. --no-exposure-warning (or S7COMMCTL_NO_EXPOSURE_WARNING) turns the warning off in environments where that is expected.
Command tree
Section titled “Command tree”s7commctl├── discover find S7 endpoints in a CIDR range├── identify SZL-based device metadata├── read read an area (DB, I, Q, M, T, C, ...)├── write write an area (requires --dangerous)├── blocks alias for list blocks├── list│ ├── blocks│ ├── db│ └── areas├── block│ ├── info│ └── upload├── diagnostic CPU state, protection level, diagnostic buffer├── fingerprint PDU size, AMQ, TSAP behaviour, CPU generation heuristic├── probe│ └── rackslot which rack/slot pairs are accepted or confirmed├── scan│ └── read-ranges which byte ranges of an area are readable├── compare│ └── read same read across several rack/slot pairs├── profile capability summary in one run├── record periodic reads to a file├── sniff decode TPKT streams arriving on a local socket├── pcap-decode offline decode of a pcap capture├── replay scripted replay endpoint for tests├── completion└── versionA safe first session
Section titled “A safe first session”When you meet an unfamiliar endpoint, start with read-only steps and widen only as needed.
-
Find the rack and slot. Setup acceptance alone is weak evidence, so ask for a follow-up query too:
Terminal window s7commctl probe rackslot --ip 192.168.1.10 --strict --first-confirmed--confirm szl,--confirm cpu-stateor--confirm anypicks the confirming query.--summary-onlyprints counts and one recommended connection tuple. The command exits 0 when it finds at least one valid pair and 1 when it finds none. -
Identify the device:
Terminal window s7commctl identify --ip 192.168.1.10 --rack 0 --slot 1 --format json -
Get a capability summary (setup, rack/slot, identity and block list) in one run:
Terminal window s7commctl profile --ip 192.168.1.10 -
Make small, bounded reads:
Terminal window s7commctl read --ip 192.168.1.10 --area DB --db 1 --offset 0 --size 16 --format hex -
Map which parts of an area respond:
Terminal window s7commctl scan read-ranges --ip 192.168.1.10 --area DB --db 1 --start 0 --end 1024 --probe-size 32
If many rack/slot pairs behave the same, compare read shows whether the endpoint is rack/slot-insensitive (a gateway or a compatibility layer, for example):
s7commctl compare read --ip 192.168.1.10 --area DB --db 1 --offset 0 --size 32 --candidates 0/0,0/1,0/2Reading
Section titled “Reading”read supports DB, I, Q, M, T and C, plus the extended areas DATA_RECORD, S7200AO, IEC_COUNTER_200 and IEC_TIMER_200. --type decodes the bytes as BOOL, INT, DINT, REAL or STRING:
s7commctl read --ip 192.168.1.10 --area M --offset 100 --size 8 --type int --format jsonThe output always shows the read status (success, short-read, empty-read, rejected and so on) and the requested and returned lengths. With --format json, a decoded array holds the typed values. Add --strict-read to make the command fail when the status is not success.
To sample a value over time, record writes one line per read:
s7commctl record --ip 192.168.1.10 --area DB --db 1 --offset 0 --size 8 \ --every-ms 1000 --count 60 --output samples.jsonlWriting
Section titled “Writing”The safety model has three rules:
- Reads are always allowed.
writerefuses to run without--dangerous.- With
--dangerous, s7commctl asks for confirmation unless you also pass--force.
# Interactive: asks before writings7commctl write --ip 192.168.1.10 --area DB --db 1 --offset 0 --type int --value 123 --dangerousUse --force only in automation where nobody can answer the prompt.
Blocks and diagnostics
Section titled “Blocks and diagnostics”s7commctl list blocks --ip 192.168.1.10 --format tables7commctl list db --ip 192.168.1.10 --format jsons7commctl block info --ip 192.168.1.10 --type DB --number 1 --format jsons7commctl block upload --ip 192.168.1.10 --type DB --number 1 --format jsons7commctl diagnostic --ip 192.168.1.10 --format tables7commctl fingerprint --ip 192.168.1.10 --format jsonBlock upload copies a block from the PLC to your machine. It does not change the PLC. Whether it is allowed depends on the CPU and its protection level. diagnostic shows the CPU state, the protection level and the diagnostic buffer, which are often more restricted than plain reads.
Offline and test tools
Section titled “Offline and test tools”pcap-decode --file capture.pcap --port 102decodes S7 frames from a packet capture on a best-effort basis. It does not reassemble TCP streams.sniff --listen :15102accepts one TCP connection on a local port and decodes the TPKT frames it receives.replay --listen :1102 --script replay.hexanswers one client with scripted response frames, one hex TPKT frame per line. It is a scaffold for deterministic tests, not a PLC simulator. For a full test server, use snap7-interop.
Output formats
Section titled “Output formats”--format takes table (human readable), json, quiet and hex. --quiet is the same as --format quiet. For discover it prints one IP address per line.
Single-target commands share one JSON envelope:
{ "target": { "ip": "192.168.1.10", "port": 102 }, "request": { "connection_mode": "rack_slot", "rack": 0, "slot": 1 }, "result": {}}On failure, an error object with code and message replaces result. discover returns a plain array with one object per device, and list blocks returns an array of blocks.
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | The target answered but the operation did not succeed (no valid rack/slot found, a read that was not success with --strict-read, a list or diagnostic that failed) |
| 2 | Usage or validation error |
| 3 | Connection or runtime failure (TCP connect failed, timeout, protocol error) |
Shell completion
Section titled “Shell completion”source <(s7commctl completion bash)source <(s7commctl completion zsh)s7commctl completion fish | sourceEnum flags such as --area, --format, --type and --confirm complete with their allowed values.