Pytjon でシリアル通信

視程計のデータを取得するために、 python でシリアル通信(RS232C)から流れてくるテキストデータを取得して保存 を目指す。

pyserial

python でシリアル通信を扱うには pyserial を使う。
参照したページ シリアル通信入門【Python】@umi_mori
pip3 install pyserial として pyserial をインストール

使おうとしたら USBシリアル変換器のドライバーがないって言われた。メーカーのページからダウンロード&インストール、Windows10 64bit でデバイスマネージャからドライバの インストール、ハードウェア変更のスキャン で使えるようになった。 COM3 
しかし、Windows10でUSBシリアルを使うのはややこしい。適当に検索して試すと動くのは動いた。うむむ。
VCP のインストールが必要とか、難しすぎて説明が書けない。再起動したら使えたりとか、わからん

さて pyserial に戻って、モジュール名は pyserial だが、import するのは serial
serial.Serial(ポート名、速度)でオープンして、with なんとかだと要らないが、そのままやるとcloseが必要。
書き方は、オブジェクト指向言語的に
with serial.Serial(port, 9600, timeout=1) as ser: とか 【 : と次の字下げに注意】
ser = serial.Serial(port, 9600, timeout=1) とか。

時刻をいれるために、time モジュールを使う(フォーマットは C言語系でOKのようだ)
受け取ったデータが空でなければ、時刻を付けて出力とすると、以下のようなものになる(と思う)。

import serial
import time
port='COM4'
format='%Y/%m/%d %H:%M:%S'
with serial.Serial(port, 9600, timeout=1) as ser:
  
  
  ser.write(b"Bytes data\r\n")
  while True:
    data=ser.readline()
    if(data==b''):
      continue
    else:
        t1=time.strftime(format, time.localtime())
        print(t1, data)
これを、ウィンドウズパワーシェルから python プログラム名 として、 クロスケーブルでつないだ、別のPC (こっちは、Teratermで接続)とすると、 送った "Byte data\r\n" が先方に出力され、先方のPCで入力した文字がこちらに表示された。
注意点は、送るのは 「バイトデータ」 なので、文字列の前に b はついている。当然受けるのもバイトデータなので、 if 文で比べる相手もバイトデータ。時刻とともに print している。(2020.1.22)

参考にしたページのいくつかは以下

ちょっと時間が経ってから作業再開

を目標に、まずは検索、
  • Python で制御文字 (control character) を削除する方法 (hawksnowlog ) こんなサンプルがあった
    つまり、
    try:
     やること
    except:
        file.close()
    
    で、Ctrl+C で抜けて、ファイルを保存して無事に終わるという動作になるようだ。 また、跡で使わない制御文字 STX などを、捨ててしまうのには以下のような処理
    import unicodedata
    
    def remove_control_characters(s):
        return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")
    
    
    
    
  • バイト列、文字列の部分文字列的な処理
    >> https://note.nkmk.me/python-str-extract/
    >> 
    >> スライスで文字列を抽出  
    >> 
    >> [start:step]とすると、start <= x <
    >> stopの範囲の文字列が抽出できる。上述のインデックスと同じく0始まり。startを省略すると先頭から、endを省略すると末尾までの範囲となる。
    >> 
    >> 
    >> インデックスと同様、負の値も使える。
    >> 
    >> バイト列でもできる( https://qiita.com/tanuk1647/items/276d2be36f5abb8ea52e )
  • 他に os ライブラリで、ディレクトリ名とファイル名の結合 (多分、OS 毎に違う区切りをうまく処理してくれる)、ディレクトリがなければ作成 などのサンプルも見つけた 結果できたのが
    シリアル通信の設定は、 ポート COM3 速度 9600bps、7ビット E (偶数)パリティであった。
    import serial
    import time
    import csv
    import unicodedata
    import os
    
    def remove_control_characters(s):
        return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")
    
    
    # DATADIR='PWD10'
    DATADIR='PWD'
    
    EXT='.csv'
    
    
    # serial open for PWD10 
    ser=serial.Serial(port='COM3',baudrate=9600,bytesize=7,parity='E', timeout=1)
    
    # set time format in data file
    format='%Y/%m/%d %H:%M:%S'
    # for filename
    # format2='%Y%m%d%H%M'  # every min
    format2='%Y%m%d%H'  # every hour
    # format2='%Y%m%d'  # every day
    
    # 前のデータファイルの名前用変数の初期化
    filename2=''
    
    print('Dir ', DATADIR, ' ?')
    if not os.path.exists(DATADIR):
      os.mkdir(DATADIR)
    
    
    try:
      while True:
        t2=time.strftime(format2, time.localtime())
        # filename=DATADIR+t2+EXT
        filename = os.path.join(DATADIR,t2)+EXT
        # print(filename)
        if(filename!=filename2):
          if(filename2!=''):
            file.close()
          file=open(filename,'a',newline='\n')
          filename2=filename
          writer = csv.writer(file)
          
            # ser.write(b"Bytes data\r\n")
          
        data=ser.readline()
        if(data==b''):
          continue
        else:
          print('Data are written into ', filename)
          t1=time.strftime(format, time.localtime())
         
          #非文字データ削除 (視程計」)
          line = t1 + ' ' + remove_control_characters(data.strip().decode('UTF=8'))
          print(line)
          lines = line.split(' ')
          writer.writerow(lines)
          file.flush()
    except:
        file.close()
            
    
    上のコード, シンプルにファイルに記録するだけ (無保証です)
    書き込みバッファをフラッシュして、すぐに書き出すようにしたほうが記録計として正しい動作 file.flush() を書いた後に追加
    ( 玉川 2021.3.29) 使い方は、python MeasVIS2.py とするだけ。データフォルダがないと作成して、format2 で指定した時間でファイルを作り、そこに追記していく。(分、時間、日 のサンプルをコメントで並べてある。) あとは、センサーに合わせて、通信設定の変更と、lines = line.split(' ') の部分を ; で分けるように直すとかの小細工で 多分、完成

    メモのページへ