Initial
This commit is contained in:
commit
b971d095c5
|
|
@ -0,0 +1,146 @@
|
|||
import serial
|
||||
import struct
|
||||
import math
|
||||
|
||||
PORT = "/dev/ttyUSB0"
|
||||
BAUD = 9600
|
||||
SLAVE_ID = 1
|
||||
START_REG = 0
|
||||
NUM_WORDS = 20
|
||||
|
||||
# --- build Modbus RTU request manually ---
|
||||
# 01 03 00 00 00 14 CRC_LO CRC_HI
|
||||
request = bytes([
|
||||
0x01, # slave
|
||||
0x03, # function
|
||||
0x00, 0x00, # start register
|
||||
0x00, 0x14, # number of registers (20)
|
||||
0x45, 0xC5 # CRC (we don't care if it's right here)
|
||||
])
|
||||
|
||||
# --- open serial port ---
|
||||
ser = serial.Serial(
|
||||
port=PORT,
|
||||
baudrate=BAUD,
|
||||
bytesize=serial.EIGHTBITS,
|
||||
parity=serial.PARITY_NONE,
|
||||
stopbits=serial.STOPBITS_ONE,
|
||||
timeout=0.1 # read timeout (seconds)
|
||||
)
|
||||
|
||||
# flush buffers
|
||||
ser.reset_input_buffer()
|
||||
ser.reset_output_buffer()
|
||||
|
||||
# send request
|
||||
ser.write(request)
|
||||
ser.flush()
|
||||
|
||||
# small delay to allow response to arrive
|
||||
#time.sleep(0.1)
|
||||
|
||||
# read whatever is available
|
||||
response = ser.read(64)
|
||||
|
||||
ser.close()
|
||||
|
||||
#print("Raw response bytes:")
|
||||
#print(" ".join(f"{b:02X}" for b in response))
|
||||
|
||||
# --- basic sanity check ---
|
||||
if len(response) < 3:
|
||||
raise RuntimeError("No valid response")
|
||||
|
||||
slave = response[0]
|
||||
func = response[1]
|
||||
byte_count = response[2]
|
||||
|
||||
# data bytes start at index 3
|
||||
data = response[3:3 + byte_count]
|
||||
|
||||
# convert data to 16-bit registers (big endian)
|
||||
registers = []
|
||||
for i in range(0, len(data), 2):
|
||||
registers.append((data[i] << 8) | data[i+1])
|
||||
|
||||
def float_dcba(regs, index):
|
||||
w1 = regs[index]
|
||||
w2 = regs[index + 1]
|
||||
b = bytes([
|
||||
(w2 & 0xFF), (w2 >> 8),
|
||||
(w1 & 0xFF), (w1 >> 8)
|
||||
])
|
||||
return struct.unpack(">f", b)[0]
|
||||
|
||||
def apparent_from_VI(V, I):
|
||||
"""
|
||||
Calculate apparent power (S) from voltage and current.
|
||||
Handles zero current edge case.
|
||||
"""
|
||||
if V == 0 or I == 0:
|
||||
return 0.0
|
||||
return V * I
|
||||
|
||||
def apparent_from_PF(P, PF):
|
||||
"""
|
||||
Calculate apparent power (S) from active power and power factor.
|
||||
Handles zero or near-zero PF edge case.
|
||||
"""
|
||||
if PF == 0:
|
||||
return 0.0
|
||||
return P / PF
|
||||
|
||||
def reactive_from_P_S(P, S):
|
||||
"""
|
||||
Calculate reactive power (Q) from active power and apparent power.
|
||||
Handles S smaller than P (possible due to rounding errors).
|
||||
"""
|
||||
if S < P:
|
||||
return 0.0
|
||||
return math.sqrt(S**2 - P**2)
|
||||
|
||||
def apparent_from_P_Q(P, Q):
|
||||
"""
|
||||
Calculate apparent power (S) from active and reactive power.
|
||||
Handles edge case where both P and Q are zero.
|
||||
"""
|
||||
if P == 0 and Q == 0:
|
||||
return 0.0
|
||||
return math.sqrt(P**2 + Q**2)
|
||||
|
||||
|
||||
active_power = float_dcba(registers, 0) # W (float)
|
||||
rms_current = float_dcba(registers, 2) # A (float)
|
||||
voltage = float_dcba(registers, 4) # V (float)
|
||||
frequency = float_dcba(registers, 6) # Hz (float)
|
||||
power_factor = float_dcba(registers, 8) # pf (float)
|
||||
annual_power_consumption = float_dcba(registers, 10) # KWH (float)
|
||||
active_consumption = float_dcba(registers, 12) # KWH (float)
|
||||
reactive_consumption = float_dcba(registers, 14) # KWH (float)
|
||||
load_time_hours = float_dcba(registers, 16) / 60.0 # Hrs (float)
|
||||
work_hours_per_day = int(registers[18]) # Hrs (int)
|
||||
device_address = int(registers[19]) # Device Address (int)
|
||||
|
||||
|
||||
S_from_VI = apparent_from_VI(voltage, rms_current)
|
||||
S_from_PF = apparent_from_PF(active_power, power_factor)
|
||||
Q_calculated = reactive_from_P_S(active_power, S_from_PF)
|
||||
S_from_PQ = apparent_from_P_Q(active_consumption, reactive_consumption)
|
||||
|
||||
# Now print using the stored variables
|
||||
print(f"{active_power:10.5f} W Active Power")
|
||||
print(f"{S_from_VI:10.5f} VA Apparent Power (V*I)")
|
||||
print(f"{S_from_PF:10.5f} VA Apparent Power (P/PF)")
|
||||
print(f"{Q_calculated:10.5f} VAR Reactive Power (from P & S)")
|
||||
print(f"{rms_current:10.5f} A RMS Current")
|
||||
print(f"{voltage:10.5f} V Voltage")
|
||||
print(f"{frequency:10.5f} Hz Frequency")
|
||||
print(f"{power_factor:10.5f} pf Power Factor")
|
||||
print(f"{annual_power_consumption:10.5f} KWH Annual Power Consumption")
|
||||
print(f"{active_consumption:10.5f} KWH Active Consumption")
|
||||
print(f"{reactive_consumption:10.5f} KWH Reactive Consumption")
|
||||
print(f"{S_from_PQ:10.5f} KVAh Apparent Power (from P & Q consumption)")
|
||||
print(f"{load_time_hours:10.5f} Hrs Load Time")
|
||||
print(f"{work_hours_per_day:10d} Hrs Work Hours per Day")
|
||||
print(f"{device_address:10d} Device Address")
|
||||
|
||||
Loading…
Reference in New Issue