0

So I have a Python MQTT program with a publisher and a subscriber code (in separate files). In my testing, I am running multiple publisher codes on Raspberry Pi 4B, and one subscriber code on my computer, where a Mosquitto broker is also located to receive the messages from the publishers. In order to analyze the network traffic afterwards, I have to capture it and I am using PyShark to do so. The clients publish 1000 messages in 3 different frequencies: 5Hz, 25Hz and 50Hz. With the network capture enabled from PyShark, the 5 and 50Hz publishes meet deadlines perfectly, while the 25Hz publish is very inconsistent and very rarely meets deadline, despite being the exact same code. Through debugging I found out that sometimes, in the 25Hz publishing, the Raspberry "locks" for a couple seconds during publishing and then resumes publishing, causing the deadline missing issues, however I find it extremely weird as it NEVER happens with either 5 or 50Hz, which is supposed to be more demanding on the hardware. The whole system is on an isolated Gigabit managed switch, so I assume the problem is not there. If I disable PyShark capture on the client code, everything runs absolutely flawlessly, but sadly I need the capture files from both sides for analysis.

I am completely clueless as to why this issue only happens with the middle publish frequency, as it would make much more sense if it happened on the 50Hz, and have tried countless things to fix it with no effect. Does anyone have any clue why this could be happening? Below are snippets of the client code, but if you need any more let me know.

# Sniffing function, responsible for executing the PyShark capture of all network traffic for further analysis in Wireshark
def sniffing_handler(self):
    # Using the Pyshark library, starts a live capture with the following options:
    # - interface -> taken from the config file, usually eth0
    # - bpf filter -> taken from the config file, should be "tcp port 1883" to only capture traffic on this port and protocol
    # - output file -> defined before the thread was started, is the file name to which the capture will be output
    sniff_duration = (self.msg_amount/self.msg_freq)+self.rtx_sleep
    self.main_logger.info(f"Sniffing thread started")
    self.main_logger.info(f"Setting up PyShark Live Capture")
    self.main_logger.info(f"Interface: {wshark_interface}")
    self.main_logger.info(f"Capture filter: {wshark_filter}")
    self.main_logger.info(f"Capture file: {os.path.basename(self.wshark_file)}")
    self.main_logger.info(f"Sniffing duration: {round(sniff_duration,2)} seconds")
    pyshark_capture = pyshark.LiveCapture(interface=wshark_interface, bpf_filter=wshark_filter, output_file=self.wshark_file)
    # Once the Live Capture object is set for this iteration, starts sniffing until the timeout is reached
    pyshark_capture.sniff(timeout=sniff_duration)

# Run handler function, used to execute each run with the information received from the server
def run_handler(self):
    self.main_logger.info(f"Run thread started")
    # In order to give the client some setup time for the PyShark capture, the client sleeps for 5 seconds before starting the run
    time.sleep(5)
    # Creates a payload with the appropriate size, and defines some variables, more specifically the publish_begin and publish_end
    # These is where the datetimes from the client-side publish measurement will be stored
    payload = bytearray(self.msg_size)
    self.pub_complete = False
    self.publish_begin = None
    self.publish_end = None
    self.main_logger.info(f"Starting publish of {self.msg_amount} messages with QoS level {self.msg_qos}")
    # Since there is a specific publish frequency to be met, the publish function may need to sleep between publishes, using the beforementioned sleep period
    # However, for better precision, instead of calculating the remaining time left for the function to sleep, the pause library is used
    # This library works with datetime objects. In order to pause the function, before every execution, a deadline is calculated
    # This deadline is increased with the sleep period everytime, and indicates until when the function must pause, with microsecond precision
    deadline = datetime.datetime.now()
    # A cycle is iterated as many times as messages that need to be published in this run
    for msg in range(self.msg_amount):
        # Updates the deadline of this iteration start with a current datetime object
        deadline += datetime.timedelta(microseconds=(self.sleep_time))
        # MQTT client publishes the messages to the main topic, with the built payload and correct QoS
        self.client.publish(main_topic, payload, qos=self.msg_qos)
        # Pauses the thread until the deadline specified is met
        pause.until(deadline)

0 Answers0