Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PySerial non-blocking read loop

I am reading serial data like this:

connected = False port = 'COM4' baud = 9600  ser = serial.Serial(port, baud, timeout=0)  while not connected:     #serin = ser.read()     connected = True      while True:         print("test")         reading = ser.readline().decode() 

The problem is that it prevents anything else from executing including bottle py web framework. Adding sleep() won't help.

Changing "while True"" to "while ser.readline():" doesn't print "test", which is strange since it worked in Python 2.7. Any ideas what could be wrong?

Ideally I should be able to read serial data only when it's available. Data is being sent every 1,000 ms.

like image 284
DominicM Avatar asked Jul 09 '13 16:07

DominicM


People also ask

What is PySerial used for?

PySerial is a library which provides support for serial connections ("RS-232") over a variety of different devices: old-style serial ports, Bluetooth dongles, infra-red ports, and so on. It also supports remote serial ports via RFC 2217 (since V2. 5).

Is PySerial full duplex?

Yes serial port hardware is full duplex. Yes, you can use threads to do Rx and Tx at the same time. Alternatively, you can use a single thread loop that does reads with a short timeout and alternates between reading and writing. Save this answer.

How do you check if you have PySerial?

To check that it is installed, start Pyzo and at the command prompt type in: import serial If it just gives you another >>> prompt, all is good. Checking that it is really working.

What is PySerial package?

It provides backends for Python running on Windows, OSX, Linux, BSD (possibly any POSIX compliant system) and IronPython. The module named “serial” automatically selects the appropriate backend. It is released under a free software license, see LICENSE for more details.


2 Answers

Using a separate thread is totally unnecessary. Just do this for your infinite while loop instead (Tested in Python 3.2.3). I use this technique in my eRCaGuy_PyTerm serial terminal program here (search the code for inWaiting() or in_waiting).

import serial import time # Optional (required if using time.sleep() below)  ser = serial.Serial(port='COM4', baudrate=9600)  while (True):     # Check if incoming bytes are waiting to be read from the serial input      # buffer.     # NB: for PySerial v3.0 or later, use property `in_waiting` instead of     # function `inWaiting()` below!     if (ser.inWaiting() > 0):         # read the bytes and convert from binary array to ASCII         data_str = ser.read(ser.inWaiting()).decode('ascii')          # print the incoming string without putting a new-line         # ('\n') automatically after every print()         print(data_str, end='')       # Put the rest of your code you want here          # Optional, but recommended: sleep 10 ms (0.01 sec) once per loop to let      # other threads on your PC run during this time.      time.sleep(0.01)  

This way you only read and print if something is there. You said, "Ideally I should be able to read serial data only when it's available." This is exactly what the code above does. If nothing is available to read, it skips on to the rest of your code in the while loop. Totally non-blocking.

(This answer originally posted & debugged here: Python 3 non-blocking read with pySerial (Cannot get pySerial's "in_waiting" property to work))

pySerial documentation: http://pyserial.readthedocs.io/en/latest/pyserial_api.html

UPDATE:

  • 27 Dec. 2018: added comment about in_waiting vs inWaiting(). Thanks to @FurkanTürkal for pointing that out in the comments below. See documentation here: https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.Serial.in_waiting.
  • 27 Oct. 2018: Add sleep to let other threads run.
  • Documentation: https://docs.python.org/3/library/time.html#time.sleep
  • Thanks to @RufusV2 for bringing this point up in the comments.

Note on multi-threading:

Even though reading serial data, as shown above, does not require using multiple threads, reading keyboard input in a non-blocking manner does. Therefore, to accomplish non-blocking keyboard input reading, I've written this answer: How to read keyboard-input?.

References:

  1. Official pySerial serial.Serial() class API - https://pyserial.readthedocs.io/en/latest/pyserial_api.html
like image 122
Gabriel Staples Avatar answered Oct 06 '22 01:10

Gabriel Staples


Put it in a separate thread, for example:

import threading import serial  connected = False port = 'COM4' baud = 9600  serial_port = serial.Serial(port, baud, timeout=0)  def handle_data(data):     print(data)  def read_from_port(ser):     while not connected:         #serin = ser.read()         connected = True          while True:            print("test")            reading = ser.readline().decode()            handle_data(reading)  thread = threading.Thread(target=read_from_port, args=(serial_port,)) thread.start() 

http://docs.python.org/3/library/threading

like image 30
Fredrik Håård Avatar answered Oct 06 '22 00:10

Fredrik Håård