Learn how to read and display RX error diagnostic counters from all network nodes in an EtherCAT network. The application creates a MotionController object, checks for errors, and if the network is operational, it reads RX error counters from each node and displays them in a formatted table.
""" EtherCAT RX error counter diagnostic utility for real-time network quality monitoring.
This utility reads the mandatory EtherCAT RX Error Counters from all network nodes following
the EtherCAT Technology Group diagnostic specifications. It monitors communication quality by
tracking both Frame Errors (CRC failures at data link layer) and Physical Layer Errors
(invalid symbols at hardware level) for each slave port.
RX Error Counter Types (Per ETG.1600)
- Frame Error Counters (0x0300, 0x0302, 0x0304, 0x0306): Increment when CRC check fails on
incoming frames, indicating corrupted data that will be discarded. Each slave port checks
frames arriving from outside and increments this counter when corruption is detected.
- Physical Layer Error Counters (0x0301, 0x0303, 0x0305, 0x0307): Track invalid symbols
detected at the physical layer, can occur both within and outside frames. When occurring
within frames, usually indicates Frame Errors as well.
When to Use This Diagnostic Tool
- Lost Frame Counter increments at master - investigate with this tool
- Working Counter (WKC) errors detected - deeper hardware diagnosis needed
- Cable qualification - validate new installations meet industrial Ethernet standards
- EMC troubleshooting - sporadic errors indicate electromagnetic interference
- Preventive maintenance - periodic monitoring for degrading connections
Interpreting Error Counter Values
- All zeros: Ideal state, network operating perfectly
- Sporadic increments (days/weeks apart): Normal due to BER 10^-12 (one bit error per trillion bits transmitted).
Industrial Ethernet standard.
- Burst errors (seconds/minutes): Actual problem requiring investigation
- Systematic increment: Hardware failure (damaged cable, connector oxidation, device fault)
- Unbalanced counters: Many Physical Layer but no Frame Errors or vice versa may indicate internal device
issue - consider replacing the device
Diagnostic Procedure (ETG Standard)
1. Follow frame path through network topology
2. Find FIRST port reporting Frame Error ≠ 0 in path sequence
3. Problem location is between this port and previous device
4. Check: cable routing near power lines, connector quality, shielding, grounding
Per ETG specifications, errors immediately after power-on/off are expected and should
be ignored. Only errors during steady-state operation indicate actual problems.
See also: ServiceChannelRead() can read ESC registers (< 0x1000) and SDOs (>= 0x1000)
"""
from typing import Dict
from _imports import RapidCode, helpers
PORT_0_FRAME_ERROR_COUNTER_REG = 0x300
PORT_0_RX_ERROR_COUNTER_REG = 0x301
PORT_1_FRAME_ERROR_COUNTER_REG = 0x302
PORT_1_RX_ERROR_COUNTER_REG = 0x303
PORT_2_FRAME_ERROR_COUNTER_REG = 0x304
PORT_2_RX_ERROR_COUNTER_REG = 0x305
PORT_3_FRAME_ERROR_COUNTER_REG = 0x306
PORT_3_RX_ERROR_COUNTER_REG = 0x307
ERROR_COUNT_SUBINDEX = 0
ERROR_COUNT_BYTES = 1
def read_node_rx_error_counters(node) -> Dict[str, int]:
"""Read all RX error counters from a network node."""
error_counts = {}
try:
error_counts['Port0_FrameErrors'] = node.ServiceChannelRead(PORT_0_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
error_counts['Port0_RxErrors'] = node.ServiceChannelRead(PORT_0_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
error_counts['Port1_FrameErrors'] = node.ServiceChannelRead(PORT_1_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
error_counts['Port1_RxErrors'] = node.ServiceChannelRead(PORT_1_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
error_counts['Port2_FrameErrors'] = node.ServiceChannelRead(PORT_2_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
error_counts['Port2_RxErrors'] = node.ServiceChannelRead(PORT_2_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
error_counts['Port3_FrameErrors'] = node.ServiceChannelRead(PORT_3_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
error_counts['Port3_RxErrors'] = node.ServiceChannelRead(PORT_3_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
except Exception as ex:
print(f"Error reading node {node.NumberGet()}: {ex}")
for port in range(4):
error_counts[f'Port{port}_FrameErrors'] = 0
error_counts[f'Port{port}_RxErrors'] = 0
return error_counts
def display_network_error_diagnostics(controller: RapidCode.MotionController):
"""Display RX error diagnostic counters for all nodes in the network."""
nodes = []
max_node_name_length = 10
for i in range(controller.NetworkNodeCountGet()):
io = controller.IOGet(i)
helpers.check_errors(io)
if io.NetworkNode.Exists():
nodes.append(io.NetworkNode)
max_node_name_length = max(max_node_name_length, len(io.NetworkNode.NameGet()))
header_line = f"{'Node':^4} | {'Name':^{max_node_name_length}} | {'Port 0':^12} | {'Port 1':^12} | {'Port 2':^12} | {'Port 3':^12}"
subheader_line = f"{'':^4} | {'':^{max_node_name_length}} | {'Frame RX':^12} | {'Frame RX':^12} | {'Frame RX':^12} | {'Frame RX':^12}"
total_width = len(header_line)
print("\n" + "="*total_width)
print("ETHERCAT RX ERROR COUNTERS")
print(f"Network: {controller.NetworkNodeCountGet()} Nodes")
print("="*total_width)
print(header_line)
print(subheader_line)
print("-"*total_width)
for node in nodes:
node_index = node.NumberGet()
node_name = node.NameGet()[:max_node_name_length]
error_counts = read_node_rx_error_counters(node)
ports = []
for port_num in range(4):
frame_errors = error_counts[f'Port{port_num}_FrameErrors']
rx_errors = error_counts[f'Port{port_num}_RxErrors']
ports.append(f"{frame_errors:5d} {rx_errors:5d}")
print(f"{node_index:^4} | {node_name:<{max_node_name_length}} | {ports[0]:^12} | {ports[1]:^12} | {ports[2]:^12} | {ports[3]:^12}")
print("="*total_width)
print("⬤ NetworkNode Errors")
print("EtherCAT RX Error Counter Diagnostics")
print("-"*40)
creation_params: RapidCode.CreationParameters = helpers.get_creation_parameters()
motion_controller: RapidCode.MotionController = RapidCode.MotionController.Create(creation_params)
print(f"MotionController creation error count: {motion_controller.ErrorLogCountGet()}")
helpers.check_errors(motion_controller)
print(f"RapidCode Version: {motion_controller.VersionGet()}")
print(f"Serial Number: {motion_controller.SerialNumberGet()}")
if motion_controller.NetworkStateGet() == RapidCode.RSINetworkState_RSINetworkStateOPERATIONAL:
display_network_error_diagnostics(motion_controller)
else:
print("\nNetwork is not in OPERATIONAL state. Current state:", motion_controller.NetworkStateGet())
print("Cannot read node error counts.")
motion_controller.Delete()