Чтение последовательных данных в реальном времени на 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 занимает так много времени, чтобы читать из последовательного порта до конца строки?
Любая помощь?
Я хочу иметь это в режиме реального времени.
Спасибо
Ответы
Ответ 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())