Configuring C-more CM5 HMI Modbus TCP Communication with Python

Brian Holt8 min read
AutomationDirectHMI ProgrammingTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

The AutomationDirect C-more CM5 series (including the CM5-T7W 7-inch widescreen model) is a full-featured HMI platform with native Ethernet and serial drivers for more than 100 PLC families, plus Modbus TCP/IP, Modbus RTU, and BACnet. The panel's Tag Name Database separates Internal tags (HMI-only scratchpad registers) from Device tags (registers bound to an external controller address). This distinction is the root cause of the most common beginner error: attempting to write to an Internal tag from a remote client and receiving a No connection could be made because the target machine actively refused it reply from Python's pylogix library.

This tutorial walks through configuring the CM5 as a Modbus TCP client that polls a Python pyModbusTCP server on the host PC. After commissioning, the same pattern can be used to display any process value (barcode, lot number, scale weight, vision system result) on a Dynamic Text object without requiring a physical PLC.

Important: The C-more panel cannot operate as a Modbus server for client-driven writes the way a CompactLogix or MicroLogix controller can. The Protocol Manager defines the panel's role as client on the network. A Python-based Modbus server is therefore required when no physical PLC exists.

Prerequisites

Item Specification
HMI panel C-more CM5-T7W (firmware 1.5x or later recommended)
Programming software C-more Programming Software v6.x or later (free download from AutomationDirect)
Ethernet connection 10/100 Base-T, panel IP on the same subnet as the host PC
Host PC Python 3.8+ on Windows 10/11 or Linux
Required Python packages pyModbusTCP>=0.2.0
Default panel IP 192.168.1.200 (CM5 ships DHCP-disabled with this static address)
Firewall rule TCP port 502 must be open inbound on the host

Reference: C-more Built-in Drivers overview (AutomationDirect video library) and the panel's User Manual / Installation Guide shipped with each unit.

Why pylogix Fails Against a CM5 Directly

pylogix speaks EtherNet/IP (CIP) and expects a Logix-family controller (CompactLogix, ControlLogix, MicroLogix 1100/1400) at the destination IP. The CM5 HMI does not run an EtherNet/IP server; its onboard protocols are listed in the table below.

Protocol Direction supported by CM5 Typical use
EtherNet/IP (CIP) Not supported as server Use an Allen-Bradley PLC between PC and HMI
Modbus TCP/IP Client only (master) Poll external Modbus servers
Modbus RTU Client only (master) RS-232/485 devices
BACnet/IP Client Building automation
DirectLOGIC / Click / Productivity Client AutomationDirect PLCs (native K-sequence, Do-more, etc.)

When the Python script opens a TCP socket to the panel's IP on port 44818 (EtherNet/IP) or 2222 (CSP), nothing is listening, so the OS returns a connection refused error. The fix is to flip the relationship: make the panel the client and put a Modbus TCP server on the PC.

Step 1 — Configure Modbus TCP in Protocol Manager

  1. Open the C-more project and select Setup → Protocol Manager (older firmware shows Panel Manager; both expose the same Modbus driver).
  2. Click Add and choose Modbus TCP/IP Client from the driver list.
  3. Assign the protocol to Port 1 (Ethernet) and configure the panel IP parameters: IP address, subnet mask, and gateway. The default IP 192.168.1.200 is acceptable for direct-to-PC links.
  4. Click PLC Interface Configuration → Add. Set the IP Address field to the host PC's address (e.g., 192.168.1.24) and the Port to 502.
  5. Set the Timeout to 1000 ms and the Retries to 3 during commissioning. Lower the timeout once the network is stable to speed up object redraws.
  6. Click OK and download the project to the panel.
Common mistake: Selecting Modbus RTU by accident. RTU requires a serial port and will not transmit on the Ethernet interface.

Step 2 — Build Modbus-Mapped Tags in the Tag Name Database

The Tag Name Database is accessed from the Project Navigator pane. For every value the HMI must display, create a row with the columns below.

Column Value for barcode demo Notes
Tag Name Usr_TapeZone_Barcode Maximum 32 alphanumeric characters, no spaces
Data Type ASCII String (length 32) or UINT array Modbus does not support strings natively; use a holding-register array
Device Name DEV001 Must match the name created in Protocol Manager
Address 400001 (function code 03, holding register 0) Use the Modbus Address format the panel expects
Tag Type Device Never Internal for external data
Read/Write Read/Write Required if the HMI needs to acknowledge the data

Create a second tag to drive the visibility trigger:

  • Tag Name: Usr_Show_TapeZone_Barcode
  • Data Type: Boolean (mapped to a single coil or holding register bit)
  • Address: 000001 (coil 0) or 400100 if you prefer holding registers

After these tags are committed, drop a Dynamic Text object on the screen, bind its Value Source to Usr_TapeZone_Barcode, and use the Visibility animation tied to Usr_Show_TapeZone_Barcode == 1.

Step 3 — Python pyModbusTCP Server

Install the package and run the example server. Port 502 is privileged on Linux; either run the script as root or rebind to 5020 and update the panel configuration to match.

# cm5_modbus_server.py
# Tested with pyModbusTCP 0.2.0, Python 3.10
from pyModbusTCP.server import ModbusServer
import time

server = ModbusServer("192.168.1.24", 502, no_block=True)

T1 = time.perf_counter()
bitValue = 0

try:
    print("Start server...")
    server.start()
    print("Server is online on 192.168.1.24:502")

    while True:
        T2 = time.perf_counter()
        if T2 - T1 >= 1.0:
            T1 = T2
            bitValue = 1 - bitValue
            print("Holding reg[0] ->", bitValue)
            # Holding register 0 maps to CM5 address 400001
            server.data_bank.set_holding_registers(0, [bitValue])
            # Optional: write a 4-char ASCII value to registers 10..11
            # 'A1B2' packed as two 16-bit words, low byte first
            server.data_bank.set_holding_registers(10, [0x3141, 0x3242])
except Exception as e:
    print("Server error:", e)
    server.stop()
    print("Server is offline")

String handling on Modbus requires packing two ASCII bytes per 16-bit register. For a 32-character barcode you must reserve 16 consecutive holding registers, then unpack in the panel by mapping each word to a substring of an ASCII String tag whose length is set to 32.

Step 4 — Wiring the Dynamic Text and Visibility

  1. Insert a Dynamic Text object on the destination screen. Under Data, set the source to Usr_TapeZone_Barcode.
  2. Open Animations → Visibility. Set the trigger condition to Usr_Show_TapeZone_Barcode == 1.
  3. If you prefer a multi-state indicator, add a Multi-State Indicator whose states are sourced from the lookup text database. C-more's Message Database Data Logging workflow applies equally well for barcode events.
  4. Compile and download the project. The panel will begin polling the Python server immediately on power-up.

Verification

Confirm the link end-to-end with these checks.

Step Action Expected result
1 Ping panel from PC Reply from 192.168.1.200 in < 1 ms
2 Run Python server, watch console "Server is online on 192.168.1.24:502"
3 From PC, run mbpoll -m tcp -t 3 -r 1 -c 1 192.168.1.24 Returns 0 or 1 (coil/holding state)
4 Trigger Usr_Show_TapeZone_Barcode from a screen pushbutton Dynamic Text appears with current barcode
5 Watch the panel's System Screen → Error Log No Modbus timeout errors logged

Troubleshooting Matrix

Symptom Likely cause Corrective action
"Connection actively refused" from pylogix pylogix uses EtherNet/IP; CM5 does not host CIP Replace pylogix with pyModbusTCP, run a server, configure CM5 as Modbus client
Tag shows ???? on screen Tag Type set to Internal in Tag Database Change Tag Type to Device and assign a valid Modbus address
Intermittent Timeout Error #E-0019 on panel PC firewall or wrong IP Allow port 502 inbound; verify PLC Interface IP matches server IP
Server bound but panel cannot read Subnet mismatch Confirm both nodes share subnet mask (e.g., 255.255.255.0); confirm gateway if cross-router
Port 502 "Permission denied" on Linux Privileged port Bind to >1024 and update Protocol Manager port to match
String displays reversed or garbled Byte order mismatch (big-endian vs little-endian) Swap nibbles when packing; CM5 expects low-byte-first for ASCII
Project downloads but tags stay at zero Wrong device number in Protocol Manager Ensure Device Name in Tag Database matches the configured PLC interface

Performance and Limits

  • The CM5 polls up to 32 Modbus tags per screen by default; additional tags may slow screen redraws.
  • Recommended minimum poll period is 100 ms per tag; below this, the panel's internal queue saturates.
  • String tags occupy 1 Modbus register per 2 ASCII characters plus 1 length register when using the panel's ASCII String type.
  • Wire the host PC with a static IP (DHCP reservations fail-over) to avoid breaking the polling loop when leases expire.

FAQ

Why does pylogix fail to connect to a C-more CM5-T7W?

The CM5 does not host an EtherNet/IP (CIP) server. pylogix sends CIP messages to TCP port 44818, which the panel does not listen on, producing a connection refused error. Configure the panel as a Modbus TCP client and use pyModbusTCP instead.

Can the CM5 act as a Modbus TCP server so my Python code can write to it directly?

No. The C-more CM5 firmware exposes Modbus only in client (master) mode. To write to the panel from a PC you must run a Modbus server on the PC (for example pyModbusTCP) and let the panel poll it.

How do I map a Python string into a CM5 Dynamic Text object?

Pack two ASCII bytes into each 16-bit Modbus holding register (low byte first), reserve enough consecutive registers to cover the string length, then create a CM5 tag of type ASCII String with the matching register count. Bind the Dynamic Text to that tag.

What Modbus address should I enter in the CM5 Tag Name Database?

Use the panel's standard notation: holding registers are entered as 4xxxxx starting at 400001, coils as 0xxxxx starting at 000001. The trailing digits map 1:1 to register numbers on the wire.

Why does my Dynamic Text object stay hidden even though the trigger coil is 1?

Confirm the visibility trigger tag points to the same Modbus address your Python server is writing, that the tag's Tag Type is Device (not Internal), and that the panel is not logging Modbus timeout errors in its system error screen.

Back to blog