Skip to content

Repository files navigation

python-s7comm

Unofficial Python implementation of the Siemens S7 communication protocol for interacting with Siemens S7 PLCs

Note: I only have an S7-1200 to test with, so I can't guarantee it works with other models (S7-300, S7-400, S7-1500).

Disclaimer: This project is provided "as is", without warranty of any kind, express or implied. The author assumes no liability for any damage, loss, downtime, or safety issues resulting from the use of this software, including but not limited to damage to PLCs, industrial equipment, production systems, or data.

This library is an unofficial, experimental implementation of the S7 protocol and is not intended for production or safety-critical use. It was created primarily for educational and research purposes.

The software has not been certified, validated, or tested for industrial deployment. Use at your own risk.

Always test thoroughly in a safe, isolated environment before connecting to live equipment.

This project is not affiliated with or endorsed by Siemens AG.

Features

  • Synchronous and Asynchronous clients - Choose between Client and AsyncClient
  • High-level API - Simple string-based addressing ("DB1.0 INT 1")
  • Low-level API - Direct access to S7Comm / AsyncS7Comm for advanced use cases
  • Read/Write operations - Single and multi-variable read/write support
  • SZL (System Status List) - Read CPU state, module identification, and other diagnostic information
  • PLC Control - Stop PLC execution
  • Type-safe - Full type hints with mypy strict mode

Installation

pip install python-s7comm

Or with uv:

uv add python-s7comm

Quick Start

Synchronous Client

frompython_s7commimportClient# Connect to PLCclient=Client()
client.connect(address="192.168.0.1", rack=0, slot=1)
# Read datadata=client.read_area("DB1.0 INT 1") # Read 1 INT from DB1 at offset 0print(int.from_bytes(data, "big", signed=True))
# Write dataclient.write_area("DB1.0 INT 1", (42).to_bytes(2, "big", signed=True))
# Read multiple variablesresults=client.read_multi_vars([
"DB1.0 INT 1",
"DB1.2 REAL 1",
"M0 BYTE 4",
])
# Get CPU statecpu_state=client.get_cpu_state()
print(f"CPU is in {cpu_state.name} mode")
# Disconnectclient.disconnect()

Using Context Manager

frompython_s7commimportClientwithClient() asclient:
client.connect(address="192.168.0.1", rack=0, slot=1)
data=client.read_area("DB1.0 BYTE 10")

Asynchronous Client

importasynciofrompython_s7commimportAsyncClientasyncdefmain():
client=AsyncClient()
awaitclient.connect(address="192.168.0.1", rack=0, slot=1)
# Read datadata=awaitclient.read_area("DB1.0 DINT 2")
# Write dataawaitclient.write_area("DB1.100 BYTE 4", b"\x01\x02\x03\x04")
# Read SZLorder_code=awaitclient.get_order_code()
print(f"PLC Order Code: {order_code}")
awaitclient.disconnect()
asyncio.run(main())

Note:AsyncClient uses an internal lock to send requests sequentially. The S7 protocol does not support concurrent requests on a single connection. But if you are brave enough, you can create multiple AsyncClient instances (e.g., one for reading, another for writing) and use them within the same event loop:

importasyncioimportstructfrompython_s7commimportAsyncClientasyncdefperiodic_reader(client: AsyncClient):
"""Reads data every 1 second - runs independently."""data=NonewhileTrue:
response=awaitclient.read_area("DB2.0 INT 1")
new_value=int.from_bytes(response, "big", signed=True)
ifnew_value!=data:
print(f"Data changed from {data} to {new_value}")
data=new_valueawaitasyncio.sleep(1)
asyncdefwriter(client: AsyncClient):
"""Performs writes - doesn't block the reader."""foriinrange(5):
awaitclient.write_area("DB2.0 INT 1", struct.pack("!H", i))
print(f"Write: {i}")
awaitasyncio.sleep(3)
asyncdefmain():
reader=AsyncClient()
writer_client=AsyncClient()
awaitreader.connect(address="192.168.0.1", rack=0, slot=1)
awaitwriter_client.connect(address="192.168.0.1", rack=0, slot=1)
# Reader and writer run concurrently without blocking each otherread_task=asyncio.create_task(periodic_reader(reader))
write_task=asyncio.create_task(writer(writer_client))
awaitwrite_task# Wait for writes to completeread_task.cancel() # Stop the periodic readerawaitreader.disconnect()
awaitwriter_client.disconnect()
asyncio.run(main())

Address Format

DB area: DB<number>.<offset>[.<bit>] <type> <count>
Other areas: <area><offset>[.<bit>] <type> <count>

Examples

AddressDescription
DB1.0 INT 11 INT from DB1 at byte 0
DB1.100 BYTE 1010 BYTEs from DB1 at byte 100
DB5.0.0 BOOL 1Bit 0 from DB5 at byte 0
M0 BYTE 44 BYTEs from Marker area
I0.0 BOOL 1Input bit 0.0
Q0 BYTE 11 BYTE from Output area

Supported Areas

AreaCodeDescription
DB0x84Data Blocks
M0x83Markers (Flags)
I0x81Inputs
Q0x82Outputs
C0x1CCounters
T0x1DTimers
P0x80Peripheral I/O

Supported Data Types

BOOL, BYTE, CHAR, WORD, INT, DWORD, DINT, REAL, COUNTER, TIMER

Connection Parameters

client=Client(
tpdu_size=1024, # COTP packet sizepdu_length=480, # S7 PDU length (negotiated)source_tsap=0x0100, # Source TSAPdest_tsap=0x0101, # Destination TSAP
)
client.connect(
address="192.168.0.1",
rack=0, # Rack numberslot=1, # Slot number (CPU slot)port=102, # ISO-on-TCP port (default: 102)
)

Requirements

  • Python 3.12 or newer

Tested On

  • Siemens S7-1200 (6ES7 214-1HG40-0XB0)

Sources

About

Unofficial implementation of the Siemens S7 protocol

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages