動作環境
以下の環境で動作確認を行いました。
- Windows10 Bulid 1809
- Python 3.6.4
- pyserial 3.4
- Python 3.6.4
セクションの複数ページをまとめています。 印刷またはPDF形式で保存...
以下の環境で動作確認を行いました。
以下のコード例では、都度 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()
受け取る辞書に関してはこちらを参照してください。
# この関数に処理したい内容を書く
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()
Class AppPAL
AppBaseを継承し、得られたペイロードを解釈して、使いやすいデータに変換して、辞書オブジェクトに登録するクラスです。
初期値が設定されているものは指定不要。
変数名 | 型 | 初期値 | 内容 |
---|---|---|---|
port | string | None | 開くシリアルポート名 例:COM3、/dev/ttyUSB0 など |
baud | int | 115200 | ボーレート |
tout | float | 0.1 | シリアル通信するときのタイムアウト時間(秒) |
sformat | string | Ascii | 本設定値はAsciiで固定 |
autolog | boolean | False | ペイロードが解釈出来たときに自動でCSVファイルにログを出力する場合はTrue |
err | boolean | False | エラーメッセージを出力する場合はTrue |
ReadSensorData()
本メソッドでペイロードを読み込んだら、 TWELITE PAL 親機の書式フォーマット に従ってそのペイロードの解釈を行います。
なし
True
False
辞書オブジェクトに格納されたデータのキーは下記の通りです。
キー | 型 | 内容 |
---|---|---|
ArriveTime | datetime | ペイロードを得たときの時間 |
LogicalID | int | 子機の論理デバイスID |
EndDeviceSID | int | 子機のシリアル番号 |
RouterSID | int | 最初に受信した中継機のシリアル番号 (親機が直接子機のパケットを受信した場合は0x80000000) |
LQI | int | 受信電波品質 |
SequenceNumber | int | パケットが送信されるごとにインクリメントされる続き番号 1からスタート、65535の次に0に戻る |
Sensor | int | センサー種別(0x80で固定) |
PALID | int | PAL基板ID |
PALVersion | int | PAL基板バージョン |
HALLIC | int | ホールICの状態 |
Temperature | float | 温度(degC) |
Humidity | float | 湿度(%) |
Illuminance | int | 照度(lux) |
AccelerationX | list,float | X軸の加速度(g) |
AccelerationY | list,float | Y軸の加速度(g) |
AccelerationZ | list,float | Z軸の加速度(g) |
SamplingFrequency | int | 加速度のサンプリング周波数 |
EventID | list,int | イベントの要因とイベントID |
WakeupFactor | list,int | 起床した要因等のデータ |
OutputCSV()
辞書オブジェクトをCSVファイルに出力します。
なし
なし
class AppBase
本コードでは、すべてのTWELITE APPS 共通で必要な機能が実装されており、シリアルデータの読み込みに必要なシリアルポートの開閉処理やシリアルデータの読み込み、ログファイルの出力などの処理が記述された基底クラスです。
これを継承した apppal.py が得られたバイト列を解釈し、辞書オブジェクトにデータを入れてメイン関数に返します。
GetDataDict()
ペイロードの解釈し、データを格納した辞書オブジェクトを返します。
なし
型 | 内容 |
---|---|
Dict | ペイロードを解釈したデータを格納した辞書オブジェクト |
Class MWSerial
本クラスはシリアルの読み書きなど、シリアルポートの管理を行うクラスです。
初期値が設定されているものは指定不要。
変数名 | 型 | 初期値 | 内容 |
---|---|---|---|
port | string | None | 開くシリアルポート名 例:COM3、/dev/ttyUSB0 など |
baud | int | 115200 | ボーレート |
timeout | float | 0.1 | シリアル通信するときのタイムアウト時間(秒) |
parity | int | serial.PARITY_NONE | パリティを指定する |
stop | int | 1 | ストップビット |
byte | int | 8 | データビット長 |
rtscts | int | 0 | RTSとCTSを有効にする場合は1 |
dsrdtr | int | 0 | DSRとDTRを有効にする場合は1 |
mode | string | Ascii | 本設定値はAsciiで固定 |
SerialSelect
PCに接続されたシリアルポートを検索し、使用するシリアルポートをユーザー選択します。
シリアルポートが1ポートしかない場合は自動的にそのポート名を使用します。
シリアルポートがない場合はNoneを指定します。
シリアルポート名を引数に指定した場合はそのシリアルポートを使用します。
変数名 | 型 | 初期値 | 内容 |
---|---|---|---|
portname | string | None | 開くシリアルポート名 (例:COM3、/dev/ttyUSB0 など) 自動選択する場合は、指定しないこと。 |
なし
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()
メソッドにより、ペイロードを取得する。
process()
などの処理により、内部格納されるペイロードは初期化や破壊されるため、速やかにデータをコピーします。なし
値 | 内容 |
---|---|
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