web-dev-qa-db-ja.com

PySerialノンブロッキング読み取りループ

私はこのようなシリアルデータを読んでいます:

_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()
_

問題は、bottle py web frameworkを含む他のすべての実行を妨げることです。 sleep()を追加しても役に立ちません。

「while True」を「while ser.readline():」に変更しても「test」は出力されません。これはPython 2.7。何が間違っているのでしょうか?

理想的には、シリアルデータが利用可能な場合にのみシリアルデータを読み取ることができるはずです。データは1,000ミリ秒ごとに送信されています。

27
DominicM

たとえば、別のスレッドに入れます。

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

40
Fredrik Håård

別のスレッドを使用する必要はまったくありません。代わりに無限のwhileループに対してこれを行ってください(Python 3.2.3)でテスト済み:

_import serial
import time # Optional (if using time.sleep() below)

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

この方法では、何かがある場合にのみ読み取りと印刷を行います。 「理想的には、シリアルデータは利用可能な場合にのみ読み取れるはずです。」これはまさに上記のコードが行うことです。読み込めるものがない場合は、whileループの残りのコードにスキップします。完全にノンブロッキング。

(この回答は元々ここに投稿され、デバッグされています: pySerialでのPython 3ノンブロッキング読み取り(pySerialの "in_waiting"プロパティを機能させることはできません)

pySerialドキュメント: http://pyserial.readthedocs.io/en/latest/pyserial_api.html

更新:

マルチスレッドに関する注意:

上記のようにシリアルデータを読み取る場合でも、notは複数のスレッドを使用し、キーボード入力を非ブロッキング方式で読み取る必要がありますdoes。したがって、非ブロッキングキーボード入力読み取りを実現するために、この答えを書きました: キーボード入力の読み方

36
Gabriel Staples

ブロッキングIOスレッドで使用することに対して警告します。覚えているPythonには [〜#〜] gil [〜#〜] =一度に1つのスレッドしか実行できませんpyserialモジュールは、シリアルポートにアクセスするOS実装のラッパーであることに注意してください。これは、Pythonの外部のコードを呼び出すことを意味します。ブロックされ、Pythonプログラム、メインスレッドでさえも実行されません。

基盤となるデバイスドライバーがタイムアウトを適切に実装していない場合、非ブロッキングIOまたはタイムアウトベースのポーリング)を使用している場合にも、これが発生する可能性があります。

より堅牢なアプローチは、 queuemultiprocessing モジュールを使用することです。別のプロセスでシリアル読み取りコードを実行します。これにより、メインおよび他のスレッドがブロックされず、プログラムが正常に終了できるようになります。

1
Mohammad Azim

タイマー駆動のイベントを使用して、シリアルポートをテストおよび読み取ります。テストされていない例:

import threading
class serialreading():
    def __init__(self):
        self.active = True
        self.test() 
    def test(self):
        n_in =comport.in_waiting()
        if n_in> 0:
            self.data = self.data + comport.read(size=n_in)
    if len(self.data) > 0: 
        print(self.data)
        self.data=""
    if self.active:
        threading.Timer(1, test).start()  # start new timer of 1 second
    def stop(self):
        self.active = False
0
William Kuipers