Reading a PLC from Python: Modbus TCP in Practice
All articles

Reading a PLC from Python: Modbus TCP in Practice

A working example: poll a Siemens S7 register every 500ms from a Python script. The bridge between the shop floor and your software stack.

February 12, 2026·7 min read·PLC Fundamentals

Modbus TCP is the lingua franca of industrial communication. It is a simple, open protocol that allows a PC running Python to request data from a PLC as easily as querying a database. For a software developer accustomed to REST APIs and JSON payloads, Modbus TCP represents a different world: one of registers, coils, and polling loops.

The following example demonstrates a practical connection to a Siemens S7 series PLC using Python. The script polls a holding register every 500 milliseconds, retrieves a process value, and prints it to the console. This pattern forms the foundation for data logging, dashboard displays, and integration with higher-level business systems.

Modbus TCP Fundamentals

Before writing code, it is useful to understand the addressing model. Modbus organizes data into four primary tables. Each table serves a distinct purpose and is accessed using a specific function code.

Data Type

Access

Function Code

Typical Use

Coils

Read-Write

01 (Read), 05 (Write)

Discrete outputs (relays, indicator lights)

Discrete Inputs

Read-Only

02

Physical sensors (limit switches, photo eyes)

Holding Registers

Read-Write

03 (Read), 06 (Write)

Analog values, setpoints, counters

Input Registers

Read-Only

04

Analog inputs (temperature, pressure)

A holding register is a 16-bit unsigned integer. When the PLC needs to expose a value such as a motor speed or a temperature reading, it places that value into a holding register at a specific offset. The Python client requests that register by its address and receives the raw integer.

Python Implementation Using PyModbus

The pymodbus library provides a clean, synchronous client for Modbus TCP communication. The following script establishes a connection, reads a target register, and repeats the operation on a fixed interval.

import time from pymodbus.client import ModbusTcpClient

# Configuration PLC_IP = "192.168.0.10" PLC_PORT = 502 REGISTER_ADDRESS = 0 POLL_INTERVAL_SECONDS = 0.5

def main(): client = ModbusTcpClient(PLC_IP, port=PLC_PORT) if not client.connect(): print("Failed to connect to PLC") return

print(f"Connected to {PLC_IP}:{PLC_PORT}") print("Polling holding register. Press Ctrl+C to stop.")

try: while True: result = client.read_holding_registers(REGISTER_ADDRESS, count=1, slave=1) if not result.isError(): raw_value = result.registers[0] print(f"Register {REGISTER_ADDRESS}: {raw_value}") else: print(f"Modbus error: {result}")

time.sleep(POLL_INTERVAL_SECONDS)

except KeyboardInterrupt: print("\nPolling stopped by user.") finally: client.close()

if __name__ == "__main__": main()

Key Considerations for Production Use

Error Handling and Resilience

Network interruptions are inevitable in an industrial environment. A production-ready script must implement retry logic and graceful degradation. The simple connection check in the example above will fail permanently if the PLC reboots. A more robust implementation would wrap the read operation in a loop that attempts reconnection after a timeout.

Data Type Interpretation

The raw register value returned by Modbus is an unsigned 16-bit integer ranging from 0 to 65535. Physical measurements such as temperature or pressure are rarely stored in this raw format. The PLC may scale the value, or it may use two consecutive registers to represent a 32-bit floating-point number. The interpretation of the register value must be agreed upon between the PLC programmer and the software developer. Common scaling factors include dividing by 10 for one decimal place of precision or applying an offset for signed values.

Polling Frequency and Scan Impact

A 500-millisecond poll interval is appropriate for dashboards and logging applications. However, a Modbus read consumes a small amount of the PLC's communication processor time. Polling hundreds of registers at millisecond speeds can impact the PLC's ability to handle other network traffic. The interval should be chosen based on the required data freshness balanced against the available bandwidth and the PLC's workload.

Bridging the Gap

The ability to read a single register from Python opens a direct path from the shop floor to the broader software ecosystem. That integer value, once retrieved, can be inserted into a time-series database like InfluxDB, streamed to a cloud platform via MQTT, or used to update a real-time web dashboard.

The protocol is decades old and intentionally simple. That simplicity is its strength. It allows a Python developer with no specialized industrial training to safely access machine data without interfering with the deterministic control logic running on the PLC. The connection is read-only by default, which is exactly what most integration scenarios require.

For software teams looking to incorporate operational data into analytics pipelines, Modbus TCP remains the most reliable and universally supported entry point.

Discussion

Sign in to join the discussion.

Sign in →

No comments yet. Be the first.