Zum Hauptinhalt springen

Modbus TCP / UDP Treiber

Überblick

PLC-Kommunikation auf Basis des Modbus-Industriestandards (Modbus-IDA / IEC 61158-6-12).

PunktWert
opc_typeMODBUS (TCP) / MODBUSUDP (UDP)
Implementierungsklasseplantpulse.driver.protocol.modbus.ModbusTCPDriver / ModbusUDPDriver
BibliothekEigene native Implementierung (plantpulse-plc-protocol mit ModbusTcp / ModbusUdp)
read
write✅ (coil / holding-register)
Sicherheitkeine (durch den Modbus-Standard bedingt)
2026-07 Umstellung auf native Implementierung (TCP + UDP)

Sowohl Modbus TCP als auch UDP wurden von der alten PLC4j-Delegation befreit und als native ModbusTcp / ModbusUdp (gemeinsame Basis AbstractNativeModbusDriver) in plantpulse-plc-protocol neu geschrieben — nach demselben Muster wie FINS / MELSEC / AB-ETH. UDP arbeitet als MBAP-over-UDP (ein Datagramm = eine ADU); Adressierung, Format und Write-Layer sind vollständig identisch mit TCP. Die Adresssyntax (holding-register:N[:TYPE]) bleibt unverändert in der PLC4X-modbus4x-Notation erhalten.


OPC-Registrierungsformular

FeldBedeutungBeispiel
opc_agent_ipIP des Modbus-Slaves192.168.0.50
opc_agent_portModbus-Port502 (TCP-Standard)
timecycleAbfrageintervall (ms)1000
options.request-timeoutAnforderungs-Timeout (ms)5000
unit-identifier

Bei Modbus TCP werden Slaves üblicherweise über IP+Port unterschieden, daher ist unit-identifier (Alias unit-id) standardmäßig 1. Befinden sich hinter einem Seriell-TCP-Gateway mehrere Slaves, wird ein Wert von 1–247 angegeben.


Tag-plc_address-Format

Adressnotation (<area>:<address> + :<datatype> + [<count>]) — die PLC4X-modbus4x-Syntax bleibt unverändert erhalten. Adressen sind 1-basiert (Umrechnung auf 0-basiert auf dem Wire, holding-register:5 → Wire-Register 4).

NotationBedeutung
holding-register:1Holding Register 1 (16 Bit)
holding-register:1:UINT16 Bit unsigned
holding-register:1:DINT32 Bit signed (2 Register)
holding-register:1:REAL32 Bit float (2 Register)
holding-register:100:STRING[10]ASCII-Zeichenkette aus 100–109
coil:1Coil, 1 Bit
discrete-input:1Discrete Input, 1 Bit
input-register:1Input Register, 16 Bit

Detaillierte Notationsregeln siehe PLC4j-Modbus-Dokumentation.


Zuordnung data_type / format

Der format des Edge wird direkt auf den Type-Suffix von PLC4j abgebildet.

edge data_typeedge formatPLC4j-addr-ZuordnungHinweis
Boolean(empty) / BOOLcoil:N oder :BOOL1 Bit
Integer / Short(empty):INT16 Bit signed
UIntegerUI / UWORD:UINT16 Bit unsigned
Integer (32)DW:DINT32 Bit signed (2 Reg.)
UInteger (32)UDW:UDINT32 Bit unsigned
FloatREAL:REAL32 Bit IEEE 754
DoubleLREAL:LREAL64 Bit IEEE 754
StringSTR[N]:STRING[N]N Zeichen

Beginnt format mit STR[N], REAL[N] oder UDINT[N], wird der Wert als Array behandelt (readWordValues).


Write (nativ — gemeinsam für TCP/UDP)

ModbusTCPDriver / ModbusUDPDriver unterstützen Schreibvorgänge (isWriteSupported()=true):

  • coil:N → Single Coil Write (Boolean).
  • holding-register:N[:TYPE] → Register-Write je nach Datentyp. REAL / DINT verwenden 2 Register in MSWord-first-Reihenfolge, alle anderen ein einzelnes 16-Bit-Register. Der Wert wird vom Aufrufer als ProtocolAddress.value übergeben.
  • discrete-input / input-register sind protokollbedingt read-only — Schreiben nicht möglich.

Häufige Fehler + Abhilfe

Meldung / SymptomUrsacheAbhilfe
PLC_READ_TIMEOUT_EXCEPTIONVerzögerte oder fehlende Antwort des SlavesKabel/Firewall/Port (502) prüfen. request-timeout erhöhen
PLC_READ_RUNTIME_EXCEPTION: Invalid PLC4j addressTippfehler in der Adressnotationholding-register:N-Format prüfen, auf fehlendes : achten
Es wird nur 0 zurückgegebenFalscher Registertyp (Input vs. Holding)Im Slave-Handbuch prüfen, ob FC 03/04/01/02 zutrifft
32-Bit-Wert vertauschtByte-/Word-ReihenfolgeIst der Slave Little-Endian, format z. B. auf :UDINT_LSWORD_FIRST ändern (PLC4j-Option)

curl-Registrierungsbeispiel (Modbus TCP)

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_MB_HVAC",
"opc_type": "MODBUS",
"opc_name": "HVAC Slave",
"opc_agent_ip": "192.168.0.50",
"opc_agent_port": "502",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "request-timeout": "5000" },
"tag_list": [
{
"tag_id": "OPC_MB_HVAC_TAG_00001",
"tag_name": "Temperature",
"plc_address": "holding-register:1:REAL",
"data_type": "Float",
"format": "REAL"
},
{
"tag_id": "OPC_MB_HVAC_TAG_00002",
"tag_name": "Status",
"plc_address": "coil:1",
"data_type": "Boolean"
}
]
}'

Bei UDP auf opc_type: "MODBUSUDP" ändern; der Port ist üblicherweise 502 oder richtet sich nach der Slave-Konfiguration.

Wert lesen:

curl -s http://<edge-host>/api/v1/tag/OPC_MB_HVAC_TAG_00001/value | jq

Beispielsammlung (nach Datentyp)

Coil / Discrete (Boolean)

data_typeformatplc_address BeispielHinweis
Boolean(empty)coil:1FC 01 (Read Coils)
Boolean(empty)discrete-input:1FC 02 (Read Discrete Inputs)
BooleanBIN[3]holding-register:1Word lesen, dann Bit 3 extrahieren

16-Bit-Ganzzahl

data_typeformatplc_address BeispielHinweis
Integer(empty)holding-register:1signed INT 16
Integer(empty)holding-register:1:INTidentisch (PLC4j-Notation)
IntegerUIholding-register:1:UINTunsigned 16
Integer(empty)input-register:1Input Register (FC 04)

32-Bit-Ganzzahl

data_typeformatplc_address BeispielHinweis
IntegerDWholding-register:1:DINTsigned 32 (2 Register)
IntegerUDWholding-register:1:UDINTunsigned 32
IntegerUDINTholding-register:132 Bit durch explizites Format

Gleitkommazahlen

data_typeformatplc_address BeispielHinweis
FloatREALholding-register:132 Bit float (2 Register)
FloatREALholding-register:1:REALidentische Notation
DoubleLREALholding-register:1:LREAL64 Bit double (4 Register)

Zeichenketten

data_typeformatplc_address BeispielHinweis
StringSTR[5]holding-register:105 Words = 10 Byte ASCII
StringSTR[16]holding-register:100:STRING[16]PLC4j-Notation

Einsatz von Formeln

Zweckdata_typefomulaHinweis
Ganzzahl-Rohwert → DezimalwertFloat${VALUE}*0.1Skalierung von Druck-/Temperatur-Rohwerten
Word-Swap-KorrekturFloat${VALUE}*1.0Für Slaves mit abweichender Byte-Reihenfolge bei 32-Bit-float. Alternative: :REAL_LSWORD_FIRST
RH/Temp-Kombination (zwei Werte in einem Word)Float${VALUE}/256nur die oberen 8 Bit

Umfassendes curl-Beispiel

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_MB_FULL",
"opc_type": "MODBUS",
"opc_name": "Modbus Full",
"opc_agent_ip": "192.168.0.50",
"opc_agent_port": "502",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "request-timeout": "5000" },
"tag_list": [
{"tag_id":"OPC_MB_FULL_T01", "tag_name":"Coil1", "plc_address":"coil:1", "data_type":"Boolean"},
{"tag_id":"OPC_MB_FULL_T02", "tag_name":"DI1", "plc_address":"discrete-input:1", "data_type":"Boolean"},
{"tag_id":"OPC_MB_FULL_T03", "tag_name":"HRSigned", "plc_address":"holding-register:1", "data_type":"Integer"},
{"tag_id":"OPC_MB_FULL_T04", "tag_name":"HRUnsigned", "plc_address":"holding-register:2:UINT", "data_type":"Integer", "format":"UI"},
{"tag_id":"OPC_MB_FULL_T05", "tag_name":"DInt", "plc_address":"holding-register:3:DINT", "data_type":"Integer", "format":"DW"},
{"tag_id":"OPC_MB_FULL_T06", "tag_name":"Temp", "plc_address":"holding-register:5:REAL", "data_type":"Float", "format":"REAL"},
{"tag_id":"OPC_MB_FULL_T07", "tag_name":"BatchName", "plc_address":"holding-register:10:STRING[10]", "data_type":"String", "format":"STR[10]"},
{"tag_id":"OPC_MB_FULL_T08", "tag_name":"PressScale", "plc_address":"holding-register:7:REAL", "data_type":"Float", "format":"REAL", "fomula":"${VALUE}*0.1"}
]
}'