Python Forum
MODBUS RS485 serial communication
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
MODBUS RS485 serial communication
#1
Sad 
Hi all,

I'm trying to send a command to my sensor and get the response back, but I'm facing these problems:
i. The time elapsed between each received/saved data is not exactly 1 second
ii. The program stops printing/saving data after a few iterations, even though the code is still running

Here is the code I'm using:

import serial
import time
import os

# Create a directory with the desired name format
folder_name = time.strftime("%H%M%S%d%m%y")
os.makedirs(folder_name, exist_ok=True)

# Establish serial connection
ser = serial.Serial('COM3', 19200, parity=serial.PARITY_EVEN, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)

# Save column headers to the file
file_path = os.path.join(folder_name, "data.txt")
with open(file_path, "a") as file:
    file.write("date_time\telapsed_seconds\tO2_ppm\tO2_percent\n")

start_time = time.time()

try:
    while True:
        # Send MODBUS command to read O2 ppm
        ser.write(b':010400000004F7\r\n')
        response = ser.readline().decode().strip()
        print(response)
        
        o2_ppm = float(response[7:15]) / 100.0  # Adjust according to the format mentioned
        print(o2_ppm)
        
        o2_percent = o2_ppm / 10000
        print(o2_percent)
        
        # Calculate elapsed seconds
        elapsed_seconds = time.time() - start_time
        
        # Get current timestamp
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        
        # Append data to the file
        with open(file_path, "a") as file:
            file.write(f"{timestamp}\t{elapsed_seconds:.2f}\t{o2_ppm:.2f}\t{o2_percent:.6f}\n")
        
        time.sleep(1)  # Wait for 1 second
        
except KeyboardInterrupt:
    pass

finally:
    # Close the serial connection
    ser.close()
Please give me some suggestions to rectify the issue. Thanks in advance.
Larz60+ write Aug-18-2023, 10:52 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Added tags for you this post, please use BBCode tags on future posts.
Reply
#2
Disclaimer: I've never used Modbus ASCII. I'm used to reading/writing binary modbus packets. I'm also more used to using TCP or UDP instead of serial. View everything below with a degree of skepticism.

Never use readline without a timeout. When your program stops responding it would be nice to know why. If the read times out, check if there any bytes in the serial buffer and display the partial results. Maybe the information can help you figure out what is going on.

You are waiting a second, of course your transactions are not happening at 1 second intervals. They are one second in-between. You could adjust your sleep time to compensate for the time it takes to perform the transaction, but it would be easier to use an event scheduler. Python comes with a built in scheduler, sched, but it is awkward to use for reoccurring events. I suggest using schedule.

https://pypi.org/project/schedule/

I would try debugging like this:
import serial
import time
import os

# Create a directory with the desired name format
folder_name = time.strftime("%H%M%S%d%m%y")
os.makedirs(folder_name, exist_ok=True)

# Establish serial connection
ser = serial.Serial(
    "COM3",
    19200,
    parity=serial.PARITY_EVEN,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=1.0,
)

# Save column headers to the file
file_path = os.path.join(folder_name, "data.txt")
with open(file_path, "a") as file:
    file.write("date_time\telapsed_seconds\tO2_ppm\tO2_percent\n")

start_time = (time.time() + 1) // 1
try:
    while True:
        time.sleep(1 - time.time() % 1)  # Wait til start of second
        ser.write(b":010400000004F7\r\n")
        response = ser.readline().decode().strip()
        if not (response := ser.readline()):
            if ser.in_waiting:
                print("Read timed out.  Buffer =", ser.read(ser.in_waiting))
            else:
                print("Read timed out.  Buffer is empty.")
            continue

        o2_ppm = float(response[7:15]) / 100.0
        o2_percent = o2_ppm / 10000
        elapsed_seconds = time.time() - start_time
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")

        # Append data to the file
        with open(file_path, "a") as file:
            # file.write(
            print(
                f"{timestamp}\t{elapsed_seconds:.2f}\t{o2_ppm:.2f}\t{o2_percent:.6f}\n"
            )

except KeyboardInterrupt:
    pass

finally:
    ser.close()
This uses sleep(), but adjusts the sleep period to compensate for the message processing/transmission time and any jitter.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020