Python Событие последовательного порта - PullRequest
0 голосов
/ 10 июля 2020

У меня есть MCU, подключенный к компьютеру через последовательный интерфейс. MCU может отправлять данные с регулярными интервалами или очень редко, в зависимости от типа подключенного к нему датчика. Поэтому я хочу иметь функцию python, которая вызывается всякий раз, когда из последовательного порта поступают данные, вместо постоянного опроса.

После прочтения множества похожих вопросов ( Маленький пример для pyserial используя Threading . PySerial / Arduino , PySerial / interrupt mode , Python Serial listener и так далее), я пришел к выводу, что решение этой проблемы - многопоточность. Итак, я придумал 3 разных кода , которые работают :

Первый:

import time
import serial
import threading

ser = serial.Serial("/dev/ttyUSB0", 19200)
datos = ""

class SerialReaderThread(threading.Thread):
'''
The class with the method that reads the serial port in the backgroud.
'''
    def __init__(self):
        super().__init__()
        self._stop_event = threading.Event()
    
    def run(self):
        '''
        The method that actually gets data from the port
        '''
        global ser, datos
        while not self.stopped():
            datos = ser.readline().decode('ascii').strip() 
    
    def stop(self):
        self._stop_event.set()
        
    def stopped(self):
        return self._stop_event.is_set()

serial_thread = SerialReaderThread()
serial_thread.start()

i = 0
while i < 5:
    if datos != "":
        print(datos)
        datos = ""
        i += 1

serial_thread.stop()

while serial_thread.isAlive():
    pass
print("Thread stopped.")
ser.close()

Второй:

import serial
import threading
import time

ser = serial.Serial("/dev/ttyUSB0", 19200)
read = True
datos = ""

def serialEvent():
    global ser, read, datos
    while read is True:
        datos = ser.read_until().decode('ascii').strip()
    return

t = threading.Thread(target=serialEvent)
t.start()

i = 0
while i < 5:
    if datos != "":
        print(datos)
        datos = ""
        i += 1

    
read = False
t.join()

while t.isAlive():
    pass
print("Thread stopped.")
ser.close()

Третий:

import serial
import concurrent

ser = serial.Serial("/dev/ttyUSB0", 19200)
datos = ""
readData = True

def serialReadEvent():
    global ser, readData, datos
    
    while readData is True:
        datos = ser.read_until().decode('ascii').strip()
    
    return
    
executor = concurrent.futures.ThreadPoolExecutor()
serialData = executor.submit( serialReadEvent )
    
i = 0
while i < 5:
    if datos != "":
        print(datos)
        datos = ""
        i += 1

readData = False
while serialData.running():
    pass

print('Thread stopped.')
ser.close()

Вопрос 1: Один из этих кодов лучше других?

Вопрос 2: использует глобальную переменную как лучший способ передать данные между потоком и основным процессом?

Я также читал, что PySerial API обеспечивает способ работы с потоками , но я не понимаю документацию.

Вопрос 3: Может ли кто-нибудь дать мне пример потока чтения с использованием PySerial API?

Наконец, я прочитал, что Последовательный порт Qt также предоставляет способ обработки входящих данных в потоке ( пример ).

Вопрос 4: - это лучший способ решить мою проблему, если я собираюсь иметь a GUI также написано на PyQt5?

1 Ответ

0 голосов
/ 11 июля 2020

Вы пришли к правильному выводу, что решение этой проблемы - Threading. Но я бы рекомендовал максимально использовать API фреймворка / библиотеки, которую вы используете.

Итак, в свете того, чего вы пытаетесь достичь, в pySerial in_waiting есть API, который вернуть количество байтов в приемном буфере.

Используя это, вы можете запустить поток, который будет непрерывно контролировать нижележащий приемный буфер и читать только тогда, когда желаемое количество байтов присутствует в буфере.

Чтобы совместно использовать полученные данные между потоком последовательного чтения и основным потоком, лучше всего использовать очереди . Основываясь на принципе FIFO, ваш поток последовательного чтения будет отвечать только за операцию постановки в очередь, а main будет отвечать только за операцию удаления из очереди. Не будет случая дублирования данных et c. В основном вы можете решить, сколько байтов должно быть в этой очереди, и только после этого вы можете исключить из очереди.

Это должно хорошо работать с вашим GUI, написанным на PyQt5.

...