2009-07-07 14 views
15

ループバックを実行しているコンピュータにArduinoを接続し、シリアルポート経由で100  ミリ秒ごとにコンピュータに戻します。pyserial - シリアルデバイスから送信された最後の行を読み取る方法

シリアルポートから数秒ごとに読み込むPythonスクリプトを作成したいので、Arduinoから送信された最後のものを見たいだけです。

Pyserialでこれをどうやってやりますか?

これは私が試したコードですが、うまくいきません。それは線を逐次読み込みます。

import serial 
import time 

ser = serial.Serial('com4',9600,timeout=1) 
while 1: 
    time.sleep(10) 
    print ser.readline() #How do I get the most recent line sent from the device? 

答えて

27

おそらく、私はあなたの質問を誤解していますが、それはシリアルラインだとして、あなたはArduinoの順次から送られたすべてのものを読んでする必要があります - あなたが読むまでそれがアルドゥイーノにまでバッファリングされますそれ。

最新のものが表示された状態を表示したい場合は、問題のコード(スリープからマイナス)を組み込んだスレッドを使用して、最後の完全な行をArduinoの最新の行として保存します。

更新:mtasicのコード例は非常に良いですが、inWaiting()が呼び出されたときにArduinoのは、部分的ラインを送信した場合、あなたは切り捨て行を取得します。代わりに、最後に行をlast_receivedに置き、部分的な行をbufferに入れて、次回のループに追加できるようにします。このような何か:

def receiving(ser): 
    global last_received 

    buffer_string = '' 
    while True: 
     buffer_string = buffer_string + ser.read(ser.inWaiting()) 
     if '\n' in buffer_string: 
      lines = buffer_string.split('\n') # Guaranteed to have at least 2 entries 
      last_received = lines[-2] 
      #If the Arduino sends lots of empty lines, you'll lose the 
      #last filled line, so you could make the above statement conditional 
      #like so: if lines[-2]: last_received = lines[-2] 
      buffer_string = lines[-1] 

readline()の使用については、「使用している場合

するように注意してください:ここでは、Pyserialのドキュメントは、(少し明確にするためとreadlinesへの言及()で編集)言っているものです読み込まれた行"。 は、 シリアルポートを開くときにタイムアウトを指定します。そうしないと、 という改行文字が受信されなかった場合、いつもブロック をブロックする可能性があります。また、 "readlines()" はタイムアウトでのみ動作します。 はタイムアウトがあることに依存し、 はそれをEOF(ファイルの終わり)として解釈します。

私にとってはかなり妥当だと思われます!

10
from serial import * 
from threading import Thread 

last_received = '' 

def receiving(ser): 
    global last_received 
    buffer = '' 

    while True: 
     # last_received = ser.readline() 
     buffer += ser.read(ser.inWaiting()) 
     if '\n' in buffer: 
      last_received, buffer = buffer.split('\n')[-2:] 

if __name__ == '__main__': 
    ser = Serial(
     port=None, 
     baudrate=9600, 
     bytesize=EIGHTBITS, 
     parity=PARITY_NONE, 
     stopbits=STOPBITS_ONE, 
     timeout=0.1, 
     xonxoff=0, 
     rtscts=0, 
     interCharTimeout=None 
    ) 

    Thread(target=receiving, args=(ser,)).start() 
+0

よく受信バッファの内容の合計を読み込みます。私の印象は、arduinoが改行によって送信しているものを区切っているので、おそらく受信バッファーのサイズと一致しないことです。 – JosefAssad

+0

last_receivedにはいつも必要なものがありますか? readlineでそれを行う方法はありますか?あなたの質問 – Greg

+0

のthats。 – mtasic85

2

送信されたすべてのものを読むにはloopが必要です。最後にreadline()を呼び出してタイムアウトまでブロックします。だから、:&ビナイSajipのコードをmtasicする

def readLastLine(ser): 
    last_data='' 
    while True: 
     data=ser.readline() 
     if data!='': 
      last_data=data 
     else: 
      return last_data 
2

若干の変更:

私は、すべての行が戻ってそのシリアルデバイスから来を必要とし、同様のアプリケーションのための私には、このコードは非常に役に立ったものの情報を定期的に送信します。

先頭から最初の要素をポップして記録し、残りの要素を新しいバッファとして再結合し、そこから続行するようにしました。

私はこれがではなく、であることを認識しています。グレッグが求めていたものですが、私はサイドノートとして共有する価値があると思いました。

def receiving(ser): 
    global last_received 

    buffer = '' 
    while True: 
     buffer = buffer + ser.read(ser.inWaiting()) 
     if '\n' in buffer: 
      lines = buffer.split('\n') 
      last_received = lines.pop(0) 

      buffer = '\n'.join(lines) 
7

これらのソリューションは、文字を待っている間にCPUを悩ますことになります。

あなたは(1)

while True: 
    if '\n' in buffer: 
     pass # skip if a line already in buffer 
    else: 
     buffer += ser.read(1) # this will block until one more char or timeout 
    buffer += ser.read(ser.inWaiting()) # get remaining buffered chars 

読み取るために少なくとも一つのブロッキング呼び出しを行う...前と同じように分割ことを行う必要があります。無限ループ内.inWaiting()を使用

2

は問題となり得ます。実装に応じて、CPU全体を占める可能性があります。代わりに、特定のサイズのデータ​​を読み込むことをお勧めします。したがって、この場合には、以下を例に行われるべきである。

ser.read(1024) 
3

このメソッドは、別途追加の行で待機しているため、各ラインのすべてのデータを収集するためのタイムアウト、および異なるタイムアウトを制御することができます。

# get the last line from serial port 
lines = serial_com() 
lines[-1]    

def serial_com(): 
    '''Serial communications: get a response''' 

    # open serial port 
    try: 
     serial_port = serial.Serial(com_port, baudrate=115200, timeout=1) 
    except serial.SerialException as e: 
     print("could not open serial port '{}': {}".format(com_port, e)) 

    # read response from serial port 
    lines = [] 
    while True: 
     line = serial_port.readline() 
     lines.append(line.decode('utf-8').rstrip()) 

     # wait for new data after each line 
     timeout = time.time() + 0.1 
     while not serial_port.inWaiting() and timeout > time.time(): 
      pass 
     if not serial_port.inWaiting(): 
      break 

    #close the serial port 
    serial_port.close() 
    return lines 
+0

ありがとうございます。私はシリアル応答から実際にすべての回線を返す他の答えを見つけることができませんでした。 – Timmay

2

あなたは、バッファ内に現在あるすべてのシリアル・データをフラッシュするser.flushInput()を使用することができます。

古いデータを消去した後、ユーザーser.readline()は、シリアルデバイスから最新のデータを取得することができます。

ここでは、他の提案されている解決策より少し簡単だと思います。私のために働いた、それはあなたに適していることを願っています。

2

あまりにも多くの合併症

改行または他の配列操作によってbytesオブジェクトを分割する理由は何ですか? 私はあなたの問題を解決する最も簡単な方法を書いています:

import serial 
s = serial.Serial(31) 
s.write(bytes("ATI\r\n", "utf-8")); 
while True: 
    last = '' 
    for byte in s.read(s.inWaiting()): last += chr(byte) 
    if len(last) > 0: 
     # Do whatever you want with last 
     print (bytes(last, "utf-8")) 
     last = '' 
関連する問題