セクションの複数ページをまとめています。 印刷またはPDF形式で保存...

もとのページに戻る

2024-11-14 現在

TWELITE PAL/CUE/ARIA Script

TWELITE SENSE PALなどからのデータを処理するPythonスクリプト
TWELITE PAL Script は、TWELITE SENSE PAL (センサーパル), TWELITE CUE (加速度センサー・磁気センサー), TWELITE ARIA (温湿度センサー・磁気センサー) からのデータの解釈、ログ記録するためのサンプルスクリプトです。

1 - 使用方法

TWELITE PAL Scriptの使用方法
TWELITE PALとMONOSTICKを使う場合の使用方法をご案内いたします。

PCの準備

  1. ダウンロードしたアーカイブをわかりやすいフォルダ(例えばC:) に解凍する。

  2. 下記WebページよりPCの環境にあったAnacondaをダウンロードし、インストールする。
    https://www.anaconda.com/download/
    Anacondaをインストールしたくない場合は”Anacondaをインストールしない場合”を参照

  3. Anaconda Promptを立ち上げ、下記コマンドを実行しpyserialをインストールする。 (Windowsの場合、Anaconda Promptは スタートメニュー → Anaconda3 内にあるのでそちらから立ち上げること。)

    
    
    pip install pyserial

Anacondaをインストールしない場合

下記のインストール手順はOSがWindowsの場合の一例です。 各種ライブラリのドキュメントをご覧の上、必要なソフトウェアおよびライブラリをインストールしてください。

  1. 下記ページよりPythonの最新版をダウンロードしインストールする。
    https://www.python.org/downloads/

    その時、下図の枠で囲まれている部分にチェックを入れること
    もしくはPythonのインストール先にPathを通すこと。

  2. コマンドプロンプトを立ち上げ、下記コマンドを入力しpyserialをインストールする。

    
    
    pip install pyserial

TWELITE PAL の準備

  1. BLUE PAL/RED PALにSENSE PALを接続する。
  2. BLUE PAL/RED PALの電池ホルダーにコイン型電池(CR2032)を挿入する。

MONOSTICK の準備

  1. MONOSTICKのアプリ(App_PAL-Parent-xxx-MONOSTICK.bin)を書き換える。
  2. MONOSTICKをリセットもしくはUSBポートに差しなおす。

スクリプトの実行方法

MONOSTICKのCOMポートがCOM6の場合、Anaconda Promptで下記コマンドを実行すると下記のようなデータが出力されます。(Teratermが接続された状態では起動できません。)


cd C:\PAL_Script
python PAL_Script.py -t COM6
*** MONOWIRELESS App_PAL_Viewer 1.1.0 ***
  *** Open COM6 ***
ArriveTime : 2021/03/05 09:43:28.880
LogicalID : 1
EndDeviceSID : 10B6465
RouterSID : No Relay
LQI : 180 (-35.50 [dBm])

スクリプトを終了させるにはAnaconda PromptでCtrl+Cを入力してください。

スクリプトの引数

スクリプト実行時に以下の引数を使用できます。

引数:-h

機能:コマンドライン引数の一覧を表示
使用例:python PAL_Script.py -h

引数:-t

機能:MONOSTICKが使用するポート名を指定
設定項目:MONOSTICKのポート名
初期値:Windowsの場合:COM3、Linuxなどの場合:/dev/ttyUSB0
使用例:python PAL_Script.py -t COM6

引数:-b

機能:MONOSTICKが使用するポートのボーレートを指定
設定項目:MONOSTICKのボーレート
初期値:115200
使用例:python PAL_Script.py -t COM6 -b 115200

引数:-l

機能:CSV形式のログを出力
使用例:python PAL_Script.py -t COM6 -l
備考:同一ディレクトリ内にCSVファイルが生成される。
ファイル名はAppPAL_シリアル番号_PAL_YYYYMMDD.csv

CSVファイルの読み方は下記表の通りです。

見出し値の説明単位
LogicalID子機の論理デバイスID-
EndDeviceSID子機のシリアルナンバー-
LQILQI-
Power電源電圧mV
ADC*電圧mV
HALLICマグネットセンサーの状態-
Temperature温度
Humidity湿度%
Illuminance照度Lux

AccelerationX
AccelerationY
AccelerationZ

加速度g

2 - ソースファイル

TWELITE PAL Scriptのソースファイル

動作環境

以下の環境で動作確認を行いました。

  • Windows10 Bulid 1809
    • Python 3.6.4
      • pyserial 3.4

2.1 - PAL_Script.py

TWELITE PAL Scriptの実行用スクリプト
本スクリプトを起動するためのコードで、MONOSTICKからのデータを読み込み、解釈されたデータの標準出力を行います。

読み出し方法

以下のコード例では、都度 MONOSTICK からデータを受信したかどうかを確認し、受信していればMain()に渡す処理を行っています。

from apppal import AppPAL

...

def mainloop(PAL):
	global end_flag

	try:
		from Main_user import Main
	except:
		mainflag = False
	else:
		mainflag = True

	if PAL.ReadSensorData():
		if mainflag:
			Main(PAL)
		else:
			PAL.ShowSensorData()

...

if __name__ == '__main__':
...

	try:
		PAL = AppPAL(port=options.target, baud=options.baud, tout=0.05, sformat=options.format, autolog=bEnableLog, err=bEnableErrMsg, stdinput=options.stdinp, Logfilename=options.file)
	except:
		print("Cannot open \"AppPAL\" class...")
		exit(1)

	while True:
		try:
			mainloop(PAL)
		except KeyboardInterrupt:
			break

	del PAL

まず、AppPALオブジェクトを生成します。オブジェクト生成時にシリアルポートの設定も行うため、シリアルポートの設定パラメータを引数として渡します。

PPAL = AppPAL(port=options.target, baud=options.baud, tout=0.05, sformat=options.format, autolog=bEnableLog, err=bEnableErrMsg, stdinput=options.stdinp, Logfilename=options.file)

次にmainloop()でシリアルデータが来ているかどうかを判断するために ReadSensorData() を呼びます。返り値が True だったら、解釈したをMain()に渡します。

def mainloop(PAL):
	# ユーザが処理を記述するMain関数がインポートできるか確認する。
	try:
		from Main_user import Main
	except:
		mainflag = False
	else:
		mainflag = True

	# データがあるかどうかの確認
	if PAL.ReadSensorData():
		if mainflag:
			# Main関数が読めたらPALオブジェクトをMain()に渡す
			Main(PAL)
		else:
			# Main関数が読めなかったらコンソールにデータを表示する。
			PAL.ShowSensorData()

受け取る辞書に関してはこちらを参照してください。

2.2 - Main_user.py

TWELITE PAL Scriptのうち、データを読み込んだ後に行う処理を記述するコード
本コードにはデータを読み込んだ後のメインの処理を記述します。 ここでは、データを受け取った際に解釈したデータをコンソールに出力するコードを記述しています。
# この関数に処理したい内容を書く
def Main(PAL=None):
	# 渡された変数がAppPALクラスか確認する。
	if isinstance(PAL, AppPAL):
		sns_data = PAL.GetDataDict()

		# 受信時間
		print('Receive Time: ', end='')
		if isinstance(sns_data['ArriveTime'], datetime.datetime):
			print(sns_data['ArriveTime'].strftime('%Y/%m/%d %H:%M:%S') + '.%03d'%(sns_data['ArriveTime'].microsecond/1000))
		else:
			print(sns_data['ArriveTime'])

		# 論理デバイスID
		print('Logical ID: 0x%02X'%sns_data['LogicalID'])
		# シリアル番号
		print('Serial ID: 0x' + sns_data['EndDeviceSID'])
		# 電源電圧
		print('Power: %d mV' % sns_data['Power'])

		# センサーの名前を調べる
		sname  = PAL.GetSensorName()

		# センサー名がPALだったらPAL/ARIA/CUE、モデル名を出力する。
		if sname == 'PAL':
			pid = PAL.GetPALName()
			print('Sensor: ' + pid )
		else:
			print('Sensor: ' + sname )

		# アナログセンサーモード(App_Tag)
		if sname == 'Analog':
			print('ADC1: %d mV'%sns_data['ADC1'])
			print('ADC2: %d mV'%sns_data['ADC2'])
		else:
			# ホールIC
			if 'HALLIC' in sns_data.keys():
				print('HALLIC: %d'%sns_data['HALLIC'])

			# 温度
			if 'Temperature' in sns_data.keys():
				print('Temperature: %.02f degC'%sns_data['Temperature'])

			# 湿度
			if 'Humidity' in sns_data.keys():
				print('Humidity: %.02f %%'%sns_data['Humidity'])

			# 照度
			if 'Illuminance' in sns_data.keys():
				print('Illuminance: %f lux'%sns_data['Illuminance'])

			# 気圧
			if 'Pressure' in sns_data.keys():
				print('Pressure: %f hPa'%sns_data['Pressure'])

			# 加速度
			if 'AccelerationX' in sns_data.keys():
				print('X: ', end='')
				print(sns_data['AccelerationX'])
				print('Y: ', end='')
				print(sns_data['AccelerationY'])
				print('Z: ', end='')
				print(sns_data['AccelerationZ'])

			# ジャイロ
			if 'Roll' in sns_data.keys():
				print('Roll: ', end='')
				print(sns_data['Roll'])
				print('Pitch: ', end='')
				print(sns_data['Pitch'])
				print('Yaw: ', end='')
				print(sns_data['Yaw'])

			# カラーセンサー
			if 'Red' in sns_data.keys():
				print('Red: ', end='')
				print(sns_data['Red'])
				print('Green: ', end='')
				print(sns_data['Green'])
				print('Blue: ', end='')
				print(sns_data['Blue'])
				print('IR: ', end='')
				print(sns_data['IR'])


		print()

2.3 - MNLib

TWELITE PAL Scriptのうち、シリアルデータの読み込みやそれを解釈するコード
TWELITE PAL Scriptのうち、シリアルデータの読み込みやそれを解釈するコードが入ったフォルダです。

2.3.1 - apppal.py

読み込んだバイト列を解釈して辞書オブジェクトに登録するクラス

Class AppPAL

AppBaseを継承し、得られたペイロードを解釈して、使いやすいデータに変換して、辞書オブジェクトに登録するクラスです。

定義するときのパラメータ

初期値が設定されているものは指定不要。

変数名初期値内容
portstringNone

開くシリアルポート名

例:COM3、/dev/ttyUSB0 など

baudint115200ボーレート
toutfloat0.1シリアル通信するときのタイムアウト時間(秒)
sformatstringAscii本設定値はAsciiで固定
autologbooleanFalseペイロードが解釈出来たときに自動でCSVファイルにログを出力する場合はTrue
errbooleanFalseエラーメッセージを出力する場合はTrue

ReadSensorData()

本メソッドでペイロードを読み込んだら、 TWELITE PAL 親機の書式フォーマット に従ってそのペイロードの解釈を行います。

パラメータ

なし

戻り値

  • データが読み込めた場合:True
  • 読み込めなかった場合:False

辞書オブジェクトに格納されたデータのキーは下記の通りです。

キー内容
ArriveTimedatetimeペイロードを得たときの時間
LogicalIDint子機の論理デバイスID
EndDeviceSIDint子機のシリアル番号
RouterSIDint

最初に受信した中継機のシリアル番号

(親機が直接子機のパケットを受信した場合は0x80000000)

LQIint受信電波品質
SequenceNumberint

パケットが送信されるごとにインクリメントされる続き番号

1からスタート、65535の次に0に戻る

Sensorintセンサー種別(0x80で固定)
PALIDintPAL基板ID
PALVersionintPAL基板バージョン
HALLICintホールICの状態
Temperaturefloat温度(degC)
Humidityfloat湿度(%)
Illuminanceint照度(lux)
AccelerationXlist,floatX軸の加速度(g)
AccelerationYlist,floatY軸の加速度(g)
AccelerationZlist,floatZ軸の加速度(g)
SamplingFrequencyint加速度のサンプリング周波数
EventIDlist,intイベントの要因とイベントID
WakeupFactorlist,int起床した要因等のデータ

OutputCSV()

辞書オブジェクトをCSVファイルに出力します。

パラメータ

なし

戻り値

なし

2.3.2 - appbase.py

シリアルデータ解釈のための基底クラス

class AppBase

本コードでは、すべてのTWELITE APPS 共通で必要な機能が実装されており、シリアルデータの読み込みに必要なシリアルポートの開閉処理やシリアルデータの読み込み、ログファイルの出力などの処理が記述された基底クラスです。
これを継承した apppal.py が得られたバイト列を解釈し、辞書オブジェクトにデータを入れてメイン関数に返します。

GetDataDict()

ペイロードの解釈し、データを格納した辞書オブジェクトを返します。

パラメータ

なし

戻り値

内容
Dictペイロードを解釈したデータを格納した辞書オブジェクト

2.3.3 - mwSerial.py

シリアルポートの管理を行うクラス

Class MWSerial

本クラスはシリアルの読み書きなど、シリアルポートの管理を行うクラスです。

定義するときのパラメータ

初期値が設定されているものは指定不要。

変数名初期値内容
portstringNone

開くシリアルポート名

例:COM3、/dev/ttyUSB0 など

baudint115200ボーレート
timeoutfloat0.1シリアル通信するときのタイムアウト時間(秒)
parityintserial.PARITY_NONEパリティを指定する
stopint1ストップビット
byteint8データビット長
rtsctsint0RTSとCTSを有効にする場合は1
dsrdtrint0DSRとDTRを有効にする場合は1
modestringAscii本設定値はAsciiで固定

SerialSelect

PCに接続されたシリアルポートを検索し、使用するシリアルポートをユーザー選択します。

シリアルポートが1ポートしかない場合は自動的にそのポート名を使用します。
シリアルポートがない場合はNoneを指定します。
シリアルポート名を引数に指定した場合はそのシリアルポートを使用します。

パラメータ

変数名初期値内容
portnamestringNone

開くシリアルポート名 (例:COM3、/dev/ttyUSB0 など)

自動選択する場合は、指定しないこと。

戻り値

なし

2.3.4 - parseFmt.py parseFmt_*.py

TWELITE シリアル書式パーサ

class FmtBase

書式パーサーの基底クラスで共通手続きを定義する。これを継承した FmtAscii (アスキー形式 ASCII形式), FmtBinary (バイナリ形式 Binary形式) を利用する。

書式パーサーは、シリアル入力を想定し、アスキー形式の場合は1行単位でバイナリ形式の場合は1バイト単位で入力系列を解釈し、その系列が書式で定義されるヘッダ、フッタ、チェックサムを満足した時、解釈の完了とし、ヘッダ、フッタを除いた内容(ペイロード)を格納する。

process(c)

入力文字列の解釈を行う。解釈後は is_complete()true を返した場合、解釈が成功し get_payload() によりペイロードを得ることができる。ペイロードは続く process() 処理などを実行すると内容を保証しないため、解釈終了後に速やかに利用します。

続けて別の系列を解釈したい場合は、そのまま process() を実行する。

パラメータ

パラメータ内容
c解釈したい入力系列。1バイト単位の解釈と系列単位の解釈の2種類に対応する。1バイト単位の入力では、int型のアスキーコード、str型、bytes型、list型の長さ1の系列。系列単位の入力では list 型、str型、bytes型の1系列を処理する。データに複数系列ある場合や途中で切れている場合は処理できない。

戻り値

なし

is_comp()

process() 処理後に呼び出し、書式解釈の完了状況を知らせる。true を得た場合は get_peyload() または get_payload_in_str() メソッドにより、ペイロードを取得する。

パラメータ

なし

戻り値

内容
true解釈に成功した。ペイロードが利用できる。
false解釈に失敗した、または、書式の途中である。

get_payload()

ペイロードを返す。

パラメータ

なし

戻り値

ヘッダやフッタが含まれないペイロード部を list型 バイト列として返します。

reinit()

明示的に内部を初期状態にします。

パラメータ

なし

戻り値

なし

その他メソッド

内部で利用する目的でいくつかのメソッドが定義されています。詳細はソースコードを参照してください。

コード例

系列単位の解釈

str 型の系列 a を解釈し、pay にペイロード情報保存します。pay には [ 0x78, 0x80, 0x01, ... , 0x00 ] が格納されます。

import parseFmt_Ascii

fmta=parseFmt_Ascii.FmtAscii()
a = ':7880010F0F0380030002800200DF'
pay = []

fmta.process(a)

if fmta.is_comp():
    pay = fmta.get_payload()

1バイト単位の解釈

バイナリの系列 b について、1バイトごと process() メソッドにより系列の解釈を進めます。終端の 0x04 を投入した時点で、解釈が完了しペイロードが pay に保管されます。

import parseFmt_Binary

fmtb=parseFmt_Binary.FmtBinary()
b = [0xA5, 0x5A, 0x80, 0x05, 0x78, 0x00, 0x11, 0x22, 0x33, 0x78, 0x04]
pay = []

for x in b:
  fmtb.process(x)

  if fmtb.is_comp():
    pay = fmtb.get_payload()
    break