Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import serial
- import struct
- import time
- # CRC-16 calculation function (Modbus flavor)
- def crc16(data):
- crc = 0xFFFF
- for byte in data:
- crc ^= byte
- for _ in range(8):
- if crc & 0x0001:
- crc = (crc >> 1) ^ 0xA001
- else:
- crc >>= 1
- return crc
- # Function to convert IEEE 754 floating point bytes to a float value
- def ieee754_to_float(bytes):
- return struct.unpack('>f', struct.pack('>L', bytes))[0]
- # Function to parse a Modbus response
- def parse_modbus_response(response_bytes):
- # Slave ID
- slave_id = response_bytes[0]
- # Function code
- function_code = response_bytes[1]
- # Byte count
- byte_count = response_bytes[2]
- # Data bytes
- data_bytes = response_bytes[3:-2]
- # CRC bytes
- crc_bytes = response_bytes[-2:]
- # Calculate CRC and check
- calculated_crc = crc16(response_bytes[:-2])
- received_crc = int.from_bytes(crc_bytes, byteorder='little')
- if calculated_crc != received_crc:
- print("CRC error in the response")
- return
- # Check for error response
- if function_code == 0x83:
- exception_code = data_bytes[0]
- print(f"Error response: Exception code {exception_code}")
- return
- # Parse data based on expected data type
- if function_code == 0x03: # Read holding registers
- if byte_count == 4: # 32-bit float
- value = ieee754_to_float(int.from_bytes(data_bytes, byteorder='big'))
- return value
- elif byte_count == 2: # 16-bit unsigned int
- value = int.from_bytes(data_bytes, byteorder='big')
- return value
- else:
- print("Unexpected byte count for read holding registers response")
- return None
- else:
- print(f"Unhandled function code: {function_code}")
- return None
- # Function to construct a Modbus request
- def construct_modbus_request(register_choice, slave_id=0x01):
- requests = {
- 1: bytes.fromhex(f'01 03 00 00 00 02'), # Total active energy (kWh)
- 7: bytes.fromhex(f'01 03 00 90 00 02'), # Grid frequency (Hz)
- }
- if register_choice in requests:
- request_bytes = requests[register_choice]
- crc = crc16(request_bytes)
- crc_bytes = crc.to_bytes(2, byteorder='little')
- return request_bytes + crc_bytes
- else:
- print("Invalid choice, please try again.")
- return None
- # Main program
- # Get the COM port from the user
- com_port = input("Enter the COM port (e.g., COM1, /dev/ttyUSB0): ")
- # Device ID is fixed to 26
- device_id = 26
- # Open the serial port
- try:
- ser = serial.Serial(com_port, baudrate=9600, bytesize=8, parity=serial.PARITY_EVEN, stopbits=1)
- except serial.SerialException as e:
- print(f"Error opening serial port: {e}")
- exit(1)
- while True:
- print("\nSelect the register you want to request:")
- print("1. Total active energy (kWh)")
- print("7. Grid frequency (Hz)")
- print("0. Exit")
- choice = int(input("Enter your choice (0, 1, or 7): "))
- if choice == 0:
- break
- request_bytes = construct_modbus_request(choice, device_id)
- if request_bytes:
- print(f"Modbus request: {request_bytes.hex()}")
- # Send the request over the serial port
- ser.write(request_bytes)
- # Wait for the response
- response_bytes = ser.read(size=9)
- if len(response_bytes) == 9:
- value = parse_modbus_response(response_bytes)
- if value is not None:
- if choice == 1:
- print(f"Total active energy (kWh): {value}")
- elif choice == 7:
- print(f"Grid frequency (Hz): {value}")
- else:
- print("Invalid response length")
- # Add a delay to avoid flooding the meter with requests
- time.sleep(0.1)
- print("Exiting program...")
- ser.close()
Advertisement
Comments
-
- Install Dependencies:
- Ensure that you have the pyserial library installed. You can install it using pip:
- pip install pyserial
- Connect the Energy Meter:
- Connect the energy meter to the device (e.g., a computer or a Raspberry Pi) running the Python code using a serial cable (e.g., RS-485 or RS-232).
- Make sure the serial connection settings (baud rate, data bits, parity, and stop bits) on both the energy meter and the device match. According to the documentation, the settings should be:
- Baud rate: 9600
- Data bits: 8
- Parity: Even
- Stop bits: 1
- Configure the Code:
- Open the Python script in a text editor or an IDE.
- Locate the construct_modbus_request function and ensure that the slave ID (device address) is set correctly. In the provided code, the slave ID is set to 0x01 (decimal 1). If your energy meter has a different slave ID, update the line:
- def construct_modbus_request(register_choice, slave_id=0x01):
- Replace 0x01 with the hexadecimal representation of your energy meter's slave ID.
- Run the Code:
- Save the Python script.
- Open a terminal or command prompt and navigate to the directory containing the Python script.
- Run the script by executing the following command:
- python script_name.py
- Replace script_name.py with the actual name of your Python script file.
- Interact with the Script:
- The script will prompt you to enter the COM port (e.g., COM1, /dev/ttyUSB0) to which the energy meter is connected. Enter the appropriate COM port and press Enter.
- The script will then display a menu with options to request different registers from the energy meter:
- Select the register you want to request:
- 1. Total active energy (kWh)
- 2. Reverse active energy (kWh)
- 3. Voltage (V)
- 4. Current (A)
- 5. Active power (kW)
- 6. Power factor
- 7. Grid frequency (Hz)
- 0. Exit
- Enter the corresponding number (1-7) for the register you want to read, and press Enter.
- The script will send the Modbus request to the energy meter and display the received response value.
- After displaying the response, the script will wait for a short period (0.1 seconds) before prompting you for the next register choice. This delay is to avoid flooding the energy meter with requests.
- To exit the script, enter 0 and press Enter.
- Interpreting the Output:
- The script will print the received value from the energy meter for the requested register.
- For 32-bit float values (e.g., voltage, current, active power), the script will display the value with the appropriate number of decimal places based on the expected accuracy.
- For 16-bit unsigned integer values (if any), the script will display the integer value directly.
Add Comment
Please, Sign In to add comment
Advertisement