センサモジュール単体でのデータの取り込み方法

センサモジュールにあらかじめ搭載されたセンサ(オンボードI2Cで接続)のデータを読み込む方法について説明します。

①センサモジュールのディップスイッチをMode-Dに設定し、電源を再投入します。

②予め接続するPCにターミナルソフト(WIN:teraterm, LINUX系:gtkterm等)をインストールしておきます。

通信コンディションは以下の通りです。

  • ポート:お使いの環境に合わせてください
  • 通信速度:1000000bps(1Mbps)
  • データ:8 bit
  • パリティー:none
  • USBケーブル(micro USB)を用いて、パソコンと接続します。

③USBケーブルでセンサモジュールとPCを接続します。
 センサモジュールには、プログラムはプリインストール(HPでも提供)されおり、USB接続(電源投入)と同時に計測が始まります。

ターミナルソフトのモニター画面に計測データが順次表示されることを確認します。図3.1にターミナルソフト(windowsPC上)の出力例を示します。文字化けなどせずにデータが正しく表示されたら、接続確認終了です。

図3.1  ターミナルソフト(TeraTerm)の出力例

 

この後は、PC内でプログラムを記述することで、シリアルポートからデータを読み込むことが出来ます。ロボットの制御等、リアルタイムフィードバックデータとしてお使いください。参考例として、python3で記述した例を示します。センサボード上に搭載した環境センサ(BME860)の温度、湿度、気圧、ガスのデータを読み込み、リアルタイムでグラフ表示します。以下に、グラフ表示の例とそのプログラムを示します。



#
# preMSMからデータを読込み、表示するプログラム
#   インタラクティブ表示可能版 グラフ縦表示版
#   Coding at 
#      Ver 0.01: 2021/07/11
#
import serial
import json
import numpy as np
import matplotlib.pyplot as plt

# メイン処理
def main():
    # 定数設定
    COM = "/dev/ttyUSB0"                            # シリアルポート名
    BaudRate = 1000000                              # Baud Rate
    loop = 2000                                     # データ取り込み回数
    sens_name = 'BME680'                            # 取得センサーの名称
    data_name = ['temp', 'humid', 'press', 'gas']   # 取得するデータの名称
    unit = ['[deg]', '[%]', '[hPa]', '[IAQ]']       # y軸の単位を設定
    color = ['r', 'g', 'b', 'm']                    # 線の色を設定
    ymin = [0, 30, 800, 0]                          # グラフY軸の最小値
    ymax = [50, 100, 1300, 100]                     # グラフY軸の最大値
    data_num  = 100                                 # 表示用データ取得数
    
    # 開始処理
    # グラフ表示用データ領域確保
    t = np.zeros(data_num)      # 時間
    data = np.zeros(len(data_name)*data_num).reshape(len(data_name), data_num)
    plt.ion()                   # グラフ表示のインタラクティブモード許可
    # グラフ表示画面設定
    fig, axes = plt.subplots(len(data_name), 1, figsize=(14, 10), sharex=True)
    graph_title = "preMSM Output (sensor : " + sens_name + ")"
    fig.suptitle(graph_title, fontsize=14)
    # グラフ表示情報をまとめる
    p_info = {"data_name":data_name, "unit":unit, "color":color, "ymin":ymin, "ymax":ymax}
    get_info_num = 0            # 取得した情報数
    print("Open Port")          # シリアル通信オープン
    with serial.Serial(COM, BaudRate) as ser:
        # データの読込みループ
        for ln in range(loop):
            try:
                line = ser.readline()                                   # preMSMのデータを1行読込み
                dict_data = json.loads(line)                            # JSON形式を辞書型に変換
                info = get_info(dict_data, sens_name, data_name)        # 辞書データから対象データを取得
                if info != False:                                       # 対象のデータがあったら
                    t, data = update_data(t, data, info, data_name)     # グラフ表示用データの更新
                    get_info_num += 1                                   # データ取得回数インクリメント
                    print('Getting data : ', get_info_num, ' / ', ln)   # 取得したデータの回数表示
                    Plot_data(t, data, p_info, fig, axes)               # グラフ表示
            except KeyboardInterrupt:
                break

    # 終了処理
    print("Close Port")         # シリアル通信クローズ

# グラフの表示
def Plot_data(t, data, p_info, fig, axes):
    color = p_info['color']                                 # 線の色を設定
    unit = p_info['unit']                                   # y軸の単位を設定
    kword = p_info['data_name']                             # データの名称
    ymin = p_info['ymin']                                   # Y軸の最小値
    ymax = p_info['ymax']                                   # Y軸の最大値

    one_dimension_axes = axes.ravel()                           # for文を使うために、1次元配列に変換
    for i, ax in enumerate(one_dimension_axes):
        line, = ax.plot(t, data[i], color=color[i], linewidth=1) # データを表示
        ax.set_xlim(min(t), max(t))                             # X軸の設定
        ax.set_ylim(ymin[i], ymax[i])                           # Y軸の設定
        if i == len(kword) - 1: 
            ax.set_xlabel('Time', fontsize=10)                  # X軸の表記
        ax.set_ylabel(kword[i]+unit[i])                         # Y軸の表記
        title = kword[i] +": "+ str(data[i][-1]) + " " + unit[i] # 最新データも表示
        ax.set_title(title)                                     # タイトルの表記
    fig.canvas.draw()
    fig.canvas.flush_events()

# グラフ表示用データ更新
def update_data(t, data, info, kword):
    buf = np.zeros(len(kword))              # 一時バッファの作成
    t = np.append(t, info['timestamp'])     # 時間情報の追加
    t = np.delete(t, 0)                     # 古いデータの削除
    for i in range(len(kword)):             # リストのデータの取り出し
        buf[i] = info[kword[i]]
    buf = buf.reshape(len(kword), 1)        # データの追加準備(行と列の変換)
    data = np.append(data, buf, axis=1)     # データの追加
    data = np.delete(data, 0, axis=1)       # 古いデータの削除
    return (t, data)

# preMSMから所得したデータから時刻と数値データを取得
def get_info(data, sname, kword):
    if data['sensor'] == sname:                         # センサーの名称が一致したら
        local_dict = {'timestamp': data['timestamp']}   # 時刻を取り出す
        for i in range(len(kword)):                     # リストのデータを取り出す
            local_dict.update({kword[i] : data[kword[i]]['val']})
        return(local_dict)                              # 作った辞書型データを返す
    else:
        return False                                    # センサーの名称が一致しなかった

if __name__ == "__main__":
    main()

通信中にはセンサモジュールのLEDが点滅しています。
通信が途絶えるなどの異常時には、WDT(Watch Dog Timer)によりセンサモジュールは自動的に再起動します。

そのため、データの先頭にあるタイムスタンプは元に戻ります。リアルタイムフィードバック系では、このタイムスタンプを使用することは稀だと思いますが、後述するIoT系のデータ処理の際には、時系列の解析などタイムスタンプに注目する用途が多いので、データ処理の際にはご注意ください。長時間連続稼働の場合も、カウンターが一周すると元に戻ります。