Skip to main content

CANopen Driver

Overview

CANopen — the EN 50325-4 / CiA 301 standard application layer on top of the CAN bus (common in motor drives / encoders / IO modules). Rewritten natively in 2026-07 — the old delegation to Apache PLC4j canopen has been removed (PLC4X CANopen only supports the local SocketCAN transport and cannot be carried over TCP, so connect died on edges without a physical CAN bus). It now uses the in-house SDO expedited upload implementation in plantpulse-plc-protocol (CanopenSdoClient) plus a transport abstraction (CanTransport) — the same native pattern as AB-ETH / BACnet.

ItemValue
opc_typeCANOPEN
Implementation classplantpulse.driver.protocol.canopen.CANOpenDriver
LibraryIn-house native (canopen in plantpulse-plc-protocol — SDO expedited upload)
InheritanceBaseProtocolDriver (not PLC4j)
getProtocol()"canopen:tcp" (retains the old PLC4X scheme notation — log/diagnostics compatibility)
Default port20200 (CAN-over-TCP gateway)
StatusStable (SDO expedited polling)
readOK (SDO expedited upload, values ≤ 4 bytes)
write❌ (read-only — SDO download not implemented)
SecurityNone
CAN-over-TCP gateway prerequisite

The default transport (tcp) communicates with CANopen nodes located beyond a CAN ↔ TCP gateway. CAN frames are carried over TCP using canId(4B BE) + dlc(1B) + data[dlc]'s own binary framing (no handshake / bus name negotiation — identical framing to the simulator). transport=socketcan (local can0) is currently a stub (requires AF_CAN native bindings / JNI), so connect fails — the physical CAN interface track is planned as follow-up.


Class structure

BaseProtocolDriver
└── CANOpenDriver — connect() 에서 CanTransport(tcp/socketcan) + CanopenSdoClient 생성

A read failure (Abort / timeout / socket) invalidates the socket (connected=false + close) so that the Edge is prompted to reconnect.


Wire format summary (in-house implementation — SDO expedited upload)

  • Request: COB-ID 0x600+nodeId, data [0x40, index LE(2B), subindex, 0×4].
  • Response: COB-ID 0x580+nodeId, command 0x43 (4B) / 0x47 (3B) / 0x4B (2B) / 0x4F (1B) — value little-endian. 0x80 = Abort (code u32 LE) → communication exception.
  • Segmented / block transfer not supported (values > 4B, e.g. long strings) — expedited is sufficient for scalar tag polling.

OPC registration options

FieldMeaningDefault
opc_agent_ipCAN-TCP gateway IP192.168.1.20
opc_agent_portGateway TCP port20200
options.read-timeoutSDO response timeout (ms)3000
options.transporttcp / socketcan (stub)tcp
options.can-interfaceInterface name in socketcan modecan0
Old PLC4X options removed

The node-id option from old forms/documents is deprecated — the node ID is now the first field of the tag address (see below). The request-timeout / heartbeat options are also not accepted by the native driver (the timeout is read-timeout).


Tag plc_address format (new contract)

<nodeId>:<index>:<subindex>[:<TYPE>]
FieldMeaningNotation
nodeIdCANopen node id (1..127)Decimal
indexObject dictionary index (0..0xFFFF)0x-prefixed hex (0x6041) or decimal
subindexSub index (0..255)0x-prefixed hex or decimal
TYPE (optional)LE byte interpretation typeIf omitted, inferred from data_type → if that is also absent, UINT16
NotationMeaning
1:0x6041:0:UINT16Node 1, DS402 Statusword
3:0x2000:0:REALNode 3, manufacturer area float32
1:0x6064:0:INT32Position actual value (32-bit signed)
5:8192:1Node 5, index decimal 8192 (= 0x2000), TYPE omitted → UINT16

TYPE tokens (aliases accepted)

Token (alias)SizeInterpretation
BOOL (BOOLEAN)1true unless 0
UINT8 (U8, USINT, BYTE) / INT8 (I8, SINT)1u8 / i8
UINT16 (U16, UINT, WORD) / INT16 (I16, INT)2u16 / i16 (default UINT16)
UINT32 (U32, UDINT, DWORD) / INT32 (I32, DINT)4u32 / i32
REAL (FLOAT, F32, REAL32)4IEEE-754 float (LE)
STRING (STR, VISIBLE_STRING)≤4ASCII (trailing NUL stripped)

When TYPE is omitted, it is inferred from data_type: Boolean→BOOL, Integer→INT16, Long→INT32, Float→REAL, Double→REAL (4B approximation), String→STRING.


Supported / unsupported

  • read: SDO expedited upload (≤ 4 bytes) — OK.
  • write: unsupportedisWriteSupported() = false and write() always return false. (SDO download is a follow-up.)
  • Segmented / block SDO, PDO mapping, NMT / Heartbeat monitoring, SYNC, EMCY, LSS: unsupported.
  • Direct SocketCAN / USB-CAN: stub (CAN-over-TCP gateway assumed).

curl registration example

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_CANOPEN_DRIVE1",
"opc_type": "CANOPEN",
"opc_name": "Servo Drive 1",
"opc_agent_ip": "192.168.1.20",
"opc_agent_port": "20200",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "read-timeout": "3000" },
"tag_list": [
{"tag_id":"OPC_CANOPEN_DRIVE1_T01","tag_name":"Statusword","plc_address":"1:0x6041:0:UINT16","data_type":"Integer"},
{"tag_id":"OPC_CANOPEN_DRIVE1_T02","tag_name":"ActualPos","plc_address":"1:0x6064:0:INT32","data_type":"Long"}
]
}'

References

  • CiA 301 (Application Layer) / CiA 402 (Drives) standards.
  • Code: plantpulse-edge-driver/src/plantpulse/driver/protocol/canopen/CANOpenDriver.java + the canopen package in plantpulse-plc-protocol.
  • Operations: the gateway framing is a proprietary binary format (canId+dlc+data) — different from the socketcand text protocol. Addresses should be validated against the device EDS file.