Beckhoff TwinCAT/ADS Driver
Overview
AMS/ADS over TCP for Beckhoff TwinCAT. Communicates directly over sockets within a single JVM, using only the AMS/ADS wire spec published by Beckhoff in infosys — no JNI or external libraries.
| Item | Value |
|---|---|
opc_type | ADS |
| Implementation class | plantpulse.driver.protocol.ads.ADSDriver |
| Library | (none — in-house implementation. Beckhoff infosys public spec) |
| read | Good (indexGroup/Offset or symbol handle) |
| write | Good (isWriteSupported() = true) |
| Default port | 48898 (ADS over TCP) |
| Default AMS port | 851 (PLC Runtime 1) |
| Security | None (direct TCP, ADS Secure not supported) |
ADS communication requires a static route registered in the AMS Router of the PC/PLC. Register the Edge's AmsNetId as a route in the TwinCAT System Manager on the PLC side; this driver then issues requests to that NetId.
Classes / Structure
ADSDriver (BaseProtocolDriver)
├── AmsTcpHeader 6 byte reserved + length(LE)
├── AmsHeader 32 byte target/source NetId+Port + cmd + state + dataLen + err + invokeId
├── AdsRead.buildRequest(indexGroup, indexOffset, length)
├── AdsWrite.buildRequest(indexGroup, indexOffset, data)
├── AdsResponse.parse(payload) — errorCode + data
├── AdsCommand — READ / WRITE / READ_STATE / READ_WRITE 상수, IGRP_*
└── AdsCodec — putUInt16/32LE, getUInt16/32LE, getInt32LE
Request-response matching uses the 32-bit invokeId issued by AtomicInteger invokeIdCounter, and requests are serialized on a single socket via synchronized sendCommand(...). Symbol name → handle conversion results are cached in symbolHandleCache (HashMap).
Wire Format Summary
AMS/TCP Header (6) | AMS Header (32) | ADS payload (가변)
└ reserved(2) len(4)│└ tgtNetId(6) tgtPort(2) srcNetId(6) srcPort(2) cmd(2) state(2) dataLen(4) err(4) invokeId(4)
- All integer fields are little-endian.
AmsNetIdis 6 bytes (e.g.5.40.40.116.1.1).cmd:READ=2,WRITE=3,READ_STATE=4,READ_WRITE=9.stateFlags:STATEFLAG_REQ_RESP = 0x0004(response required).
OPC Registration Options (options)
| Key | Meaning | Default |
|---|---|---|
target-netid | PLC AmsNetId, e.g. 5.40.40.116.1.1 | host + .1.1 |
target-port | AMS target port (PLC Runtime) | 851 |
source-netid | Local AmsNetId | 127.0.0.1.1.1 |
source-port | Local AMS port | 32905 |
connect-timeout | Connection timeout (ms) | 3000 |
read-timeout | Response wait timeout (ms) | 3000 |
Immediately after connect(), the handshake is verified by calling READ_STATE — on failure, connected=false.
Tag Address Formats
| Notation | Meaning | indexGroup |
|---|---|---|
M0:4 | %M offset 0, 4 bytes | IGRP_PLC_RW_MB (0x4020) |
I0:2 | %I offset 0, 2 bytes | IGRP_PLC_RW_IB (0x4000) |
Q0:1 | %Q offset 0 | IGRP_PLC_RW_QB (0x4030) |
DB10:4 | DB10 offset 0 | IGRP_PLC_RW_DB (0x4040) |
0x4020:0x10:4 | Direct indexGroup:indexOffset:length | (as-is) |
MAIN.fCounter:REAL | Symbolic (ReadWrite + IGRP_GET_SYMHANDLE_BYNAME → cached) | (handle-based) |
After : you may specify a byte length or a TwinCAT type keyword such as BOOL / INT / DINT / REAL / LREAL / STRING, and sizeOfTypeKeyword(...) determines the byte length.
Data Encoding / Decoding
decode(byte[], data_type) / encode(value, data_type, hintLen) handle little-endian:
data_type | bytes | Notes |
|---|---|---|
| Boolean / Bool | 1 | data[0] != 0 |
| Byte | 1 | unsigned |
| Short / Int / Integer / Word | 2 | signed 16 |
| UInt16 | 2 | unsigned |
| Int32 / DWord | 4 | signed 32 |
| UInt32 | 4 | unsigned |
| Float / REAL | 4 | IEEE 754 |
| Long / Int64 / LWord | 8 | signed |
| Double / LREAL | 8 | IEEE 754 |
| String | N | UTF-8, NUL-terminated handling |
Supported / Not Supported
- ✅ Memory area read/write (M / I / Q / DB)
- ✅ Symbol name → handle (
IGRP_GET_SYMHANDLE_BYNAME=0xF003) →IGRP_RW_SYMVAL_BYHANDLE(0xF005) - ✅
READ_STATE(ADS state + Device state) - ❌ ADS Notification (subscription)
- ❌ SUMUP (wrapping multiple reads into one)
- ❌ ADS Secure / TLS
Test Coverage
test/java/plantpulse/driver/protocol/ads/
| Class | Test count |
|---|---|
AdsAddressParseTest | 10 |
AdsCodecTest | 11 |
AdsReadTest | 5 |
AdsResponseTest | 5 |
AdsSpecComplianceTest | 23 |
AdsWriteTest | 5 |
AmsHeaderTest | 6 |
AmsNetIdTest | 7 |
AmsTcpHeaderTest | 7 |
79 tests in total.
References
- Beckhoff infosys: TwinCAT ADS / AMS over TCP wire format
- Code:
src/plantpulse/driver/protocol/ads/