Skip to content

Commit 580083e

Browse files
authored
Added no-GUI Pymodbus Python Script
1 parent a850f44 commit 580083e

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

pymodbus_nogui.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# ==================== PYMODBUS (RTU/TCP) ====================
2+
3+
import csv
4+
import time
5+
import os
6+
from datetime import datetime
7+
from pymodbus.client import ModbusTcpClient, ModbusSerialClient
8+
9+
# ==================== CONFIGURATION ====================
10+
11+
# Choose Modbus Type: "tcp" for Ethernet, "rtu" for Serial
12+
MODBUS_TYPE = "rtu" # Change to "rtu" if using Serial (RS-485)
13+
14+
# Modbus TCP (Ethernet) Configuration
15+
PLC_IP = "192.168.1.10" # Change to your PLC's IP Address
16+
PLC_PORT = 502 # Default Modbus TCP Port
17+
18+
# Modbus RTU (Serial) Configuration RS-485
19+
SERIAL_PORT = "COM3" # Windows: COM3, Linux: "/dev/ttyUSB0"
20+
BAUDRATE = 9600 # Adjust based on PLC settings
21+
PARITY = "O" # None (N), Even (E), or Odd (O)
22+
STOPBITS = 1
23+
BYTESIZE = 8
24+
TIMEOUT = 1
25+
26+
# Modbus Register Settings
27+
REGISTER_ADDRESS = 0x6304 # Change based on PLC's register map
28+
REGISTER_COUNT = 1 # Number of registers to read
29+
UNIT_ID = 1 # Usually 1 for a single PLC
30+
31+
# CSV File Name
32+
CSV_FILE = "plc_data.csv"
33+
34+
# Data Logging Interval (Seconds)
35+
LOG_INTERVAL = 5 # Change to your desired logging frequency
36+
37+
# Max reconnection attempts
38+
MAX_RECONNECT_ATTEMPTS = 3
39+
40+
# ==================== CONNECT TO PLC ====================
41+
42+
def connect_to_plc():
43+
"""Connects to the PLC using either Modbus TCP or Modbus RTU."""
44+
try:
45+
if MODBUS_TYPE == "tcp":
46+
client = ModbusTcpClient(PLC_IP, port=PLC_PORT)
47+
else:
48+
# Updated for newer pymodbus versions
49+
client = ModbusSerialClient(
50+
port=SERIAL_PORT,
51+
baudrate=BAUDRATE,
52+
parity=PARITY,
53+
stopbits=STOPBITS,
54+
bytesize=BYTESIZE,
55+
timeout=TIMEOUT,
56+
)
57+
58+
if client.connect():
59+
print(f"✅ Connected to PLC via Modbus {MODBUS_TYPE.upper()}")
60+
return client
61+
else:
62+
print("❌ Failed to connect to PLC")
63+
return None
64+
65+
except Exception as e:
66+
print(f"❌ Exception while connecting to PLC: {str(e)}")
67+
return None
68+
69+
# ==================== READ DATA FROM PLC ====================
70+
71+
def read_plc_data(client):
72+
"""Reads data from PLC coils."""
73+
try:
74+
response = client.read_coils(address=6304,count= 1) # Read 1 coil at address 6304
75+
76+
if response.isError():
77+
print(f"❌ Modbus Error: {response}")
78+
return None
79+
else:
80+
return response.bits # ✅ Correct attribute for coils (True/False list)
81+
82+
except Exception as e:
83+
print(f"❌ Exception while reading PLC: {str(e)}")
84+
return None
85+
86+
87+
# ==================== SAVE DATA TO CSV ====================
88+
89+
def initialize_csv():
90+
"""Creates a new CSV file with headers if it doesn't exist."""
91+
if not os.path.exists(CSV_FILE):
92+
with open(CSV_FILE, mode="w", newline="") as file:
93+
writer = csv.writer(file)
94+
headers = ["Timestamp"] + [f"Register_{REGISTER_ADDRESS + i}" for i in range(REGISTER_COUNT)]
95+
writer.writerow(headers)
96+
print(f"✅ Created new CSV file: {CSV_FILE}")
97+
98+
def save_to_csv(data):
99+
"""Saves PLC data to a CSV file with timestamps."""
100+
if data is None:
101+
print("⚠️ No data to save")
102+
return
103+
104+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
105+
106+
# Open CSV in append mode
107+
with open(CSV_FILE, mode="a", newline="") as file:
108+
writer = csv.writer(file)
109+
writer.writerow([timestamp] + data)
110+
111+
print(f"✅ Data logged at {timestamp}: {data}")
112+
113+
# ==================== MAIN LOOP ====================
114+
115+
def main():
116+
"""Continuously reads and logs PLC data."""
117+
# Initialize CSV file with headers
118+
initialize_csv()
119+
120+
# Connect to PLC
121+
client = connect_to_plc()
122+
if client is None:
123+
retry_count = 0
124+
while client is None and retry_count < MAX_RECONNECT_ATTEMPTS:
125+
print(f"Retrying connection ({retry_count + 1}/{MAX_RECONNECT_ATTEMPTS})...")
126+
time.sleep(5) # Wait before retry
127+
retry_count += 1
128+
client = connect_to_plc()
129+
130+
if client is None:
131+
print("Failed to connect after multiple attempts. Exiting.")
132+
return
133+
134+
print("📡 Starting data logging... (Press CTRL+C to stop)")
135+
136+
try:
137+
reconnect_count = 0
138+
while True:
139+
data = read_plc_data(client)
140+
141+
# Handle connection loss during operation
142+
if data is None and reconnect_count < MAX_RECONNECT_ATTEMPTS:
143+
print(f"Connection may be lost. Attempting to reconnect ({reconnect_count + 1}/{MAX_RECONNECT_ATTEMPTS})...")
144+
client.close()
145+
time.sleep(2)
146+
client = connect_to_plc()
147+
reconnect_count += 1
148+
if client is None:
149+
continue
150+
elif data is None:
151+
print("Too many failed read attempts. Exiting.")
152+
break
153+
else:
154+
reconnect_count = 0 # Reset counter on successful read
155+
156+
save_to_csv(data)
157+
time.sleep(LOG_INTERVAL) # Wait before next read
158+
159+
except KeyboardInterrupt:
160+
print("\n🛑 Stopping data logging...")
161+
finally:
162+
if client is not None:
163+
client.close()
164+
print("Connection closed. Exiting.")
165+
166+
if __name__ == "__main__":
167+
main()

0 commit comments

Comments
 (0)