Подтвердить что ты не робот

Чтение последовательных данных в реальном времени на Python

Я использую script в Python для сбора данных с микроконтроллера PIC через последовательный порт со скоростью 2 Мбит/с.

PIC работает с идеальной синхронизацией на скорости 2 Мбит/с, а также последовательный порт USB FTSI отлично работает на скорости 2 Мбит/с (оба проверены с помощью осциллографа)

Im посылает сообщения (размер около 15 символов) около 100-150x раз в секунду, а число увеличивается (чтобы проверить, не потеряны ли сообщения и т.д.)

На моем ноутбуке у меня Xubuntu работает как виртуальная машина, я могу прочитать последовательный порт через Putty и через мой script (python 2.7 и pySerial)

Проблема:

  • При открытии последовательного порта через Putty я вижу все сообщения (счетчик в сообщении увеличивает 1 на 1). Отлично!
  • При открытии последовательного порта через pySerial я вижу все сообщения, но вместо того, чтобы получать 100-150x в секунду, я получаю их примерно 5 в секунду (все равно сообщение увеличивается с шагом 1 на 1), но они, вероятно, хранятся в некотором буфере, как когда Я отключаю ПИК, я могу пойти на кухню и вернуться, и я все еще получаю сообщения.

Вот код (я пропустил большую часть кода, но цикл тот же):

ser = serial.Serial('/dev/ttyUSB0', 2000000, timeout=2, xonxoff=False, rtscts=False, dsrdtr=False) #Tried with and without the last 3 parameters, and also at 1Mbps, same happens.
ser.flushInput()
ser.flushOutput()
While True:
  data_raw = ser.readline()
  print(data_raw)

Кто-нибудь знает, почему pySerial занимает так много времени, чтобы читать из последовательного порта до конца строки? Любая помощь?

Я хочу иметь это в режиме реального времени.

Спасибо

4b9b3361

Ответ 1

Вы можете использовать inWaiting(), чтобы получить количество байтов, доступных во входной очереди.

Затем вы можете использовать read() для чтения байтов, что-то вроде этого:

While True:
    bytesToRead = ser.inWaiting()
    ser.read(bytesToRead)

Почему бы не использовать readline() в этом случае из Документов:

Read a line which is terminated with end-of-line (eol) character (\n by default) or until timeout.

Ожидание таймаута при каждом чтении, так как он ждет eol. последовательный вход Q остается тем же самым, что он просто занимает много времени, чтобы добраться до "конца" буфера. Чтобы понять его лучше: вы пишете на вход Q, как гоночный автомобиль, и читаете как старый автомобиль:)

Ответ 2

При открытии последовательного порта необходимо установить тайм-аут на "Нет":

ser = serial.Serial(**bco_port**, timeout=None, baudrate=115000, xonxoff=False, rtscts=False, dsrdtr=False) 

Это команда блокировки, поэтому вы ожидаете, пока не получите данные с новой строкой (\n или \r\n) в конце: line = ser.readline()

После того, как у вас есть данные, он вернется как можно скорее.

Ответ 3

Из руководство:

Возможные значения для тайм-аута параметра:... x установить время ожидания до x секунд

и

readlines (sizehint = None, eol = '\n') Прочитайте список строк, до тайм-аута. sizehint игнорируется и присутствует только для API совместимость со встроенными файловыми объектами.

Обратите внимание, что эта функция возвращает только время ожидания.

Таким образом, ваш readlines вернется максимум через каждые 2 секунды. Используйте read(), как предложил Тим.

Ответ 4

Очень хорошее решение для этого можно найти здесь:

class ReadLine:
    def __init__(self, s):
        self.buf = bytearray()
        self.s = s

    def readline(self):
        i = self.buf.find(b"\n")
        if i >= 0:
            r = self.buf[:i+1]
            self.buf = self.buf[i+1:]
            return r
        while True:
            i = max(1, min(2048, self.s.in_waiting))
            data = self.s.read(i)
            i = data.find(b"\n")
            if i >= 0:
                r = self.buf + data[:i+1]
                self.buf[0:] = data[i+1:]
                return r
            else:
                self.buf.extend(data)

ser = serial.Serial('COM7', 9600)
rl = ReadLine(ser)

while True:

    print(rl.readline())

Ответ 5

Очень хорошее решение для этого можно найти здесь:

Здесь класс, который служит оболочкой для объекта pyserial. Это позволяет читать строки без 100% загрузки процессора. Он не содержит никакой логики тайм-аута. Если происходит тайм-аут, self.s.read(i) возвращает пустую строку, и вы можете self.s.read(i) исключение, чтобы указать тайм-аут.

Это также должно быть быстро, по словам автора:

Приведенный ниже код дает мне 790 кБ/с, а замена кода методом pyserial readline - всего 170 кБ/с.

class ReadLine:
    def __init__(self, s):
        self.buf = bytearray()
        self.s = s

    def readline(self):
        i = self.buf.find(b"\n")
        if i >= 0:
            r = self.buf[:i+1]
            self.buf = self.buf[i+1:]
            return r
        while True:
            i = max(1, min(2048, self.s.in_waiting))
            data = self.s.read(i)
            i = data.find(b"\n")
            if i >= 0:
                r = self.buf + data[:i+1]
                self.buf[0:] = data[i+1:]
                return r
            else:
                self.buf.extend(data)

ser = serial.Serial('COM7', 9600)
rl = ReadLine(ser)

while True:

    print(rl.readline())